Skip to main content

Persistent Sheets

Persistent sheets are pre-mounted bottom sheets that stay in memory even when closed. They open instantly without mount delay and preserve their internal state between open/close cycles.

When to Use

  • Frequently accessed sheets - Scanner, camera, quick actions
  • State preservation - Forms, multi-step wizards, media players
  • Instant open - When mount delay is noticeable

Basic Usage

import {
BottomSheetPersistent,
useBottomSheetControl,
} from 'react-native-bottom-sheet-stack';

function App() {
return (
<BottomSheetManagerProvider id="main">
<BottomSheetScaleView>
<HomeScreen />
</BottomSheetScaleView>
<BottomSheetHost />

{/* Persistent sheet - always mounted */}
<BottomSheetPersistent id="scanner">
<ScannerSheet />
</BottomSheetPersistent>
</BottomSheetManagerProvider>
);
}

function HomeScreen() {
const scanner = useBottomSheetControl('scanner');

return (
<Button
title="Open Scanner"
onPress={() => scanner.open({ scaleBackground: true })}
/>
);
}

State Preservation

Internal component state is preserved between open/close cycles:

const ScannerSheet = forwardRef((props, ref) => {
const { close } = useBottomSheetContext();
// State is preserved when sheet is closed and reopened
const [scanResult, setScanResult] = useState<string | null>(null);
const [isScanning, setIsScanning] = useState(false);

const handleScan = () => {
setIsScanning(true);
// ... scanning logic
setScanResult('QR-ABC123');
setIsScanning(false);
};

return (
<BottomSheetManaged ref={ref} enableDynamicSizing>
<View>
{scanResult ? (
<Text>Result: {scanResult}</Text>
) : (
<Button title="Start Scan" onPress={handleScan} />
)}
<Button title="Close" onPress={close} />
</View>
</BottomSheetManaged>
);
});

Close the sheet and reopen it - scanResult is still there.

Comparison with Portal API

FeatureBottomSheetPortalBottomSheetPersistent
Context preservation
State preservation❌ (remounts)
Instant open
Memory usageLowerHigher
Best forRegular sheetsFrequently used sheets

Passing Params

You can pass params when opening:

const scanner = useBottomSheetControl('scanner');

scanner.open({
scaleBackground: true,
params: { source: 'home', title: 'Scan QR Code' },
});

Access them in the sheet:

const ScannerSheet = forwardRef((props, ref) => {
const { params } = useBottomSheetContext<'scanner'>();

return (
<BottomSheetManaged ref={ref}>
<Text>{params?.title ?? 'Scanner'}</Text>
</BottomSheetManaged>
);
});

Type Safety

Register your persistent sheet IDs for type-safe params:

// bottom-sheet.d.ts
declare module 'react-native-bottom-sheet-stack' {
interface BottomSheetPortalRegistry {
scanner: { source: 'home' | 'navigation'; title?: string };
}
}

Now useBottomSheetControl('scanner') has typed open() and params.

Placement

BottomSheetPersistent can be placed anywhere inside BottomSheetManagerProvider - at the app root, on a specific screen, or nested deep in your component tree.

The only requirement: it must stay mounted to be accessible. If you unmount it, the sheet won't be available until it mounts again.

// Option 1: At app root - always available
<BottomSheetManagerProvider id="main">
<BottomSheetScaleView>
<App />
</BottomSheetScaleView>
<BottomSheetHost />
<BottomSheetPersistent id="scanner">
<ScannerSheet />
</BottomSheetPersistent>
</BottomSheetManagerProvider>

// Option 2: On a specific screen - available only when screen is mounted
function HomeScreen() {
return (
<View>
<BottomSheetPersistent id="quick-actions">
<QuickActionsSheet />
</BottomSheetPersistent>
{/* ... screen content */}
</View>
);
}
tip

For sheets that should be accessible from anywhere in your app, place them at the root level to ensure they're always mounted.