Detached

A floating card — inset from the sides, resting above the safe area, every corner rounded — that closes as one rigid body.

import { BottomSheet } from "@delacour/react-native-bottom-sheet";
import { StyleSheet, Text, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";

const styles = StyleSheet.create({
  overlay: { backgroundColor: "#00000099" },
  background: { backgroundColor: "#2C2C2E", borderRadius: 24 },
  handle: { alignItems: "center", paddingBottom: 8, paddingTop: 10 },
  pill: { backgroundColor: "#8E8E93", borderRadius: 2, height: 4, width: 36 },
  content: { gap: 12, paddingBottom: 20, paddingHorizontal: 20, paddingTop: 4 },
  title: { color: "#FFFFFF", fontSize: 20, fontWeight: "700" },
  description: { color: "#8E8E93", fontSize: 15, lineHeight: 21 },
  primary: { alignItems: "center", backgroundColor: "#30D158", borderRadius: 12, paddingVertical: 14 },
  primaryLabel: { color: "#FFFFFF", fontSize: 15, fontWeight: "600" },
});

export function FloatingCard() {
  const insets = useSafeAreaInsets();

  return (
    <BottomSheet bottomInset={insets.bottom} detached>
      <BottomSheet.Trigger>
        <Text>Open a floating card</Text>
      </BottomSheet.Trigger>
      <BottomSheet.Portal>
        <BottomSheet.Overlay style={styles.overlay} />
        <BottomSheet.Container>
          <BottomSheet.Background style={styles.background} />
          <BottomSheet.Handle style={styles.handle}>
            <View style={styles.pill} />
          </BottomSheet.Handle>
          <BottomSheet.Content style={styles.content}>
            <BottomSheet.Title style={styles.title}>Turn on notifications?</BottomSheet.Title>
            <BottomSheet.Description style={styles.description}>
              A card that floats above the safe area, with room on every side. Tap outside it to close.
            </BottomSheet.Description>
            <BottomSheet.Close style={styles.primary}>
              <Text style={styles.primaryLabel}>Allow</Text>
            </BottomSheet.Close>
          </BottomSheet.Content>
        </BottomSheet.Container>
      </BottomSheet.Portal>
    </BottomSheet>
  );
}

detached floats the sheet as a card. true is { horizontalMargin: 16, bottomOffset: 16 }: 16 in from each side, resting 16 above bottomInset. The corners are yours — borderRadius on the Background rounds all four, because the surface is exactly the sheet's height tall.

<BottomSheet detached={{ horizontalMargin: 24, bottomOffset: 32 }}>

Geometry

The card rests bottomOffset + bottomInset above the frame's bottom edge — the resting line. Every snap point is a height above that line, so "50%" is half of the height between the resting line and the top inset, and the card's snap points read the same as an attached sheet's.

  +--------------------------------------+ <-- the frame's top         ^
  |                                      |                             |
  |     +--------------------------+     |  ^                          |
  |     |           ====           |     |  |                          | available
  |<--->| the card                 |<--->|  | height, a snap point     |
  |     |                          |     |  |                          |
  |     +--------------------------+     |  v  <-- the resting line    v
  | horizontalMargin                     |  ^
  |                                      |  |  bottomOffset
  |  - - - - - - - - - - - - - - - - -   |  |               restingBottom, the gap
  |               ______                 |  |  bottomInset
  +--------------------------------------+  v  <-- the frame's bottom
restingBottom = bottomOffset + bottomInset
available     = containerHeight − restingBottom
closed        = translateY = containerHeight        the card's top edge at the frame's bottom, fully off-screen

Closed is fully off-screen: not resting on the line with zero height, but below the frame entirely. The band under a sticky footer is zero — the card already floats above the inset — and the keyboard lift subtracts the gap, so a card lifting for a keyboard lands its footer on the keyboard's edge like an attached sheet does.

The overlay covers the margins

The overlay fills the frame, margins and gap included, and the panel is box-none where the card is not, so a tap beside the card or in the gap under it reaches the overlay and closes. A card with no Overlay leaves those areas to the screen behind.

The card moves as one rigid body

A drag down to close, the close animation, a rubber band under the lowest snap point and an over-drag past the highest all move the whole card — the top corners, the copy, the footer and the bottom corners together — through the gap and off the screen. Nothing clips or shrinks at the resting line.

The panel is as tall as the frame, so the card's surface is sized separately: the sheet's height between its snap points, the lowest snap point's height below them, the highest's above them. The body is clamped to what the surface shows. That is what keeps the bottom corners real while the card travels, and it is why a detached Background needs no fixed height.

Attached or detached

attached (default)detached
Rests onthe frame's bottom edgebottomOffset above bottomInset
Widththe frame'sthe frame's less two margins
Corners to roundthe top twoall four
Safe-area bandbottomInset, under the content or the footernone — the card floats above it
Closedheight 0, on the bottom edgefully off-screen
A tap in the gap—closes, through the overlay

API Reference

detached

Prop

Type

On this page