iOS sheet

A native modal bottom sheet with a centered title, grabber and deliberate entrance.

Preferences

Choose when to receive updates.

Notifications

Edit a focus

Studio

Edit focus

Choose a name and notification preference.

Use it for

  • A focused modal task presented from the bottom edge.
  • Keep the sheet mounted to preserve form drafts while closed.

Not for

  • Claiming the grabber is draggable; it is a visual indicator.
  • Using a modal for ordinary page navigation or passive status.

Three ways in. They share one source, so the pixels match whichever you pick.

1. Import the package

npm install @noorddev/vlak-react

// once, next to your app's root
import "@noorddev/vlak-react/css";

import { IOSSheet } from "@noorddev/vlak-react";

Precompiled React and one stylesheet. No compiler to configure. Per-component imports work too: @noorddev/vlak-react/components/ios-sheet.

2. Vendor the source

npx @noorddev/vlak-cli add ios-sheet

The StyleX leaf lands in components/vlak/ with its dependencies, for your own compiler to own. See StyleX.

3. Through shadcn

npx shadcn add https://vlak.dev/r/ios-sheet.json

The same registry item, installed by shadcn's CLI.

CSS only

<link rel="stylesheet" href="node_modules/@noorddev/vlak/css/vlak.css" />

No React. Link vlak.css and use the markup and classes below.

import { IOSSheet } from "@noorddev/vlak-react";

<IOSSheet title="Edit note" open={open} onOpenChange={setOpen} description="Changes stay on this device."><form id="note-editor">…</form></IOSSheet>

IOSSheet

A modal bottom sheet. The platform owns the focus trap and top layer.

PropTypeDefaultDescription
openboolean
defaultOpenbooleanfalse
onOpenChange(open: boolean) => void
onClose() => void
titlerequiredReactNode
descriptionReactNode
closeLabelstring"Close sheet"
dismissablebooleantrue
lightDismissbooleantrue

Also accepts Omit<DialogHTMLAttributes<HTMLDialogElement>, "open" | "onClose" | "title">.

KeysDoes
Tab, Shift+TabUses the native modal focus trap.
EscapeRequests closing unless dismissable is false or onCancel prevents it.
Enter, Space on CloseCloses a dismissable sheet and restores its trigger when still available.
<dialog class="rs-ios-sheet" aria-labelledby="sheet-title"><div class="rs-ios-sheet-handle" aria-hidden="true"></div><header class="rs-ios-sheet-header"><span></span><h2 class="rs-ios-sheet-title" id="sheet-title">Edit note</h2><button class="rs-ios-sheet-close" type="button" aria-label="Close sheet">×</button></header><div class="rs-ios-sheet-body">Note content</div></dialog>
.rs-ios-sheet.rs-ios-sheet-open.rs-ios-sheet-handle.rs-ios-sheet-header.rs-ios-sheet-title.rs-ios-sheet-close.rs-ios-sheet-body.rs-ios-sheet-description