๐ŸŽ‰ True Sheet 4.0 is out with a rewritten layout engine. Read the announcement ยท Migration guide
โ† All posts

True Sheet 4.0

True Sheet 4.0 rewrites the layout engine for synchronous per-detent layout, adds first-class Expo Router support, overlays, custom backgrounds, and dim customization.

๐ŸŽ‰ True Sheet 4.0 is here!

Version 4 rewrites the sheet's layout engine. The sheet now lays out synchronously per detent, so the container is sized to the sheet's visible height and tracks it in realtime while dragging. On top of that, 4.0 brings first-class Expo Router support, overlays above sheets, custom backgrounds, and more than 30 bug fixes.

๐Ÿ“ A New Layout Engine

In v3, the content was sized to the largest detent, and resizing lagged a frame behind the sheet. In v4, the content resizes in the same frame as the sheet, on every platform:

  • Sized per detent โ€” The container matches the sheet's visible height at each detent
  • Tracks the drag โ€” Flex layouts, like a bottom-pinned button, follow the sheet's edge frame by frame
  • Natural layout โ€” Content wraps its children's height, like a regular view. Pass flex: 1 via style to fill the sheet

Requirements:

  • React Native >= 0.82 (Expo SDK 55+)
  • New Architecture enabled

โœจ What's New

๐Ÿ“ auto Detent with Scrollables

The scrollable prop is replaced by scrollableRef. Pass a ref of your ScrollView or FlatList to wire nested scrolling, keyboard insets, and auto sizing. With an auto detent, the sheet sizes to the scroll content and resizes as it grows or shrinks:

const scrollableRef = useRef<ScrollView>(null)

<TrueSheet scrollableRef={scrollableRef} detents={['auto']}>
  <ScrollView ref={scrollableRef}>{/* ... */}</ScrollView>
</TrueSheet>

The scrollable's bottom inset is now applied natively: the safe-area inset while the content can scroll, plus the height of an absolute footer floating over it. No more manual paddingBottom.

See the Detents guide and Scrolling guide for more details.

๐Ÿฆถ Relative Footers and Floating Headers

Footers now take up space below the content by default and absorb the bottom safe-area inset. Set footerOptions.position to 'absolute' to float the footer over the content, like in v3.

Headers get the same option: set headerOptions.position to 'absolute' to float the header over the content and exclude it from the auto detent.

<TrueSheet
  header={<MyHeader />}
  headerOptions={{ position: 'absolute' }}
  footer={<MyFooter />}
  footerOptions={{ position: 'absolute' }}
>
  {/* content */}
</TrueSheet>

See the Header guide and Footer guide for more details.

๐Ÿงญ First-Class Expo Router Support

The sheet navigator is now built on standard-navigation, so one implementation works with both React Navigation and Expo Router. Expo Router apps get a ready-to-use Sheet layout from the new /navigation/expo-router entry point:

// app/_layout.tsx
import { Sheet } from '@lodev09/react-native-true-sheet/navigation/expo-router'

export default function SheetLayout() {
  return (
    <Sheet>
      <Sheet.Screen name="index" />
      <Sheet.Screen name="details" options={{ detents: ['auto', 1] }} />
    </Sheet>
  )
}

It requires Expo SDK 57+ and works on Router 58. See the Navigation guide for more details.

๐Ÿž Overlays Above Sheets

The new TrueSheetOverlay component renders its children in a native layer above every presented sheet. Use it for toasts and dialogs instead of the FullWindowOverlay / Modal workaround:

import { TrueSheetOverlay } from '@lodev09/react-native-true-sheet'

<TrueSheetOverlay>
  {visible && <Dialog onClose={() => setVisible(false)} />}
</TrueSheetOverlay>

See the Overlays guide for more details.

๐Ÿ–ผ๏ธ Custom Backgrounds

The new background and backgroundStyle props work on iOS, Android, and Web. Use backgroundStyle.backgroundColor for an exact color or a tint over glass or blur, and pass any element to background for a custom one. detentBackgrounds changes the background per detent, with native transitions on iOS 26.1+:

<TrueSheet
  detents={['peek', 'auto', 1]}
  detentBackgrounds={[null, { blur: 'system-material' }, '#18202b']}
/>

See the Backgrounds guide for more details.

๐ŸŽจ Dim Color and Opacity

Customize the dim behind the sheet with dimColor and dimOpacity:

<TrueSheet detents={['auto', 1]} dimColor="#1b0a3c" dimOpacity={0.3}>
  {/* content */}
</TrueSheet>

See the Dimming guide for more details.

๐Ÿ›‘ Confirm Dismissals

The new onDismissAttempt event fires when the user tries to dismiss a sheet with dismissible set to false. Confirm unsaved changes, then call dismiss():

<TrueSheet
  ref={sheet}
  dismissible={!hasUnsavedChanges}
  onDismissAttempt={() => confirmDiscard(() => sheet.current?.dismiss())}
>
  {/* form */}
</TrueSheet>

See the Dismissing guide for more details.

๐Ÿ“ฑ Foldables

On iOS 27.1+ foldables, foldOptions rests the sheet on the hinge when the device is half-folded:

<TrueSheet
  detents={['peek', 0.6, 1]}
  foldOptions={{ hingeDetentIndex: 1, hingeBehavior: 'snap' }}
>
  {/* content */}
</TrueSheet>

See the Foldables guide for more details.

โž• And More

  • ๐Ÿ‘‡ dismissThreshold โ€” Set it to 'short' to dismiss on a short drag or fling on Android and Web, like Compose Material3's sheet
  • ๐Ÿ’ค lazy โ€” Set it to false to mount the content before the sheet presents
  • โŒจ๏ธ autoFocus โ€” An input with autoFocus now raises the keyboard together with the sheet
  • ๐Ÿ™ˆ footerOptions.avoidKeyboard โ€” Keep an absolute footer behind the keyboard
  • ๐Ÿ“œ scrollableOptions.keyboardOffset โ€” Adjust the scrollable's bottom inset while the keyboard is open
  • โ†”๏ธ placement โ€” Replaces anchor. The new 'automatic' default uses the native placement on iOS 27+
  • ๐Ÿ” Element inspector โ€” The dev menu inspector now works inside a presented sheet
  • ๐Ÿ”— Deep linking โ€” A new Deep Linking guide for cold-start deep links to sheets
  • ๐Ÿ†• Expo SDK 58 โ€” Supports React Native 0.88

๐Ÿ› Bug Fixes

4.0 includes more than 30 fixes across iOS, Android, and Web, including keyboard handling, stacked sheets, iPad windowing, accessibility, and the sheet navigator. See the release notes for the full list.

๐Ÿ’ฅ Breaking Changes

Version 4 includes some breaking changes:

  • React Native 0.82+ required โ€” The new layout engine relies on synchronous Fabric state updates
  • scrollableRef โ€” Replaces the scrollable prop
  • Natural content layout โ€” Pass flex: 1 via style to fill the sheet
  • Relative footers โ€” Set footerOptions.position to 'absolute' to keep a floating footer
  • Native insets โ€” Remove manual safe-area padding from footers and scroll content
  • placement โ€” Replaces anchor, and 'left' / 'right' become 'leading' / 'trailing'
  • Backgrounds โ€” blurOptions is removed, and blur now takes precedence over backgroundColor on iOS
  • Navigation โ€” The sheet navigator requires the standard-navigation peer dependency and @react-navigation/native 7.3+

Check out the Migration Guide for the full list and step-by-step upgrade instructions.

๐Ÿš€ Get Started

yarn add @lodev09/react-native-true-sheet@^4.0.0

Or with Expo:

npx expo install @lodev09/react-native-true-sheet

v3 is still supported for React Native 0.81-0.86 (Expo SDK 54-57). Install @lodev09/react-native-true-sheet@^3 and see the v3 docs.

Running into issues? Check out the Troubleshooting guide for common fixes.

๐Ÿ™ Thank You!

A huge thanks to everyone who contributed to this release: @maxlapides, @SamuelBrucksch, @IjzerenHein, @1kuko3, @vilindberg, @simondaigre, @ronnymajani, @nickcernera, @hukpo, @cpruijsen, @billouboq, @MAVERlCK and @AndriiSerheniuk. And thanks to everyone who tested the betas, reported issues, and shared feedback.

Have feedback? Open an issue!