Sheet
Sheet is part of a collection of components, also including Modal, Dialog and Drawer, that overlay content on top of a full-page view and often block the UI below. A Modal presents content, tasks, or actions that require exclusive user focus.
Design guidance
When and how to use this
A Sheet should be preferred over the other overlay components for mobile-first layouts, especially those surfaced within native applications.
When to consider something else
If the content to be displayed in the overlay needs to take advantage of additional screen space on tablet and desktop screen sizes, consider using a Modal with responsive layout for the content.
React
Render the Sheet component itself unconditionally using the isOpen
and
onClose
props to control its open state. The
useDisclosure
is purpose-built to manage
the open state of overlays.
Scrollable Content
When displaying long-form content in a Sheet, you likely want to restrict
the height of the scrollable container on mobile when the Sheet is anchored
to the bottom of the screen. Pass maxContentHeight
to control the maximum
height of the scroll container.
Prop | Description |
---|---|
header | Sets the heading content. The value can be a simple string, in which case it is rendered as the appropriate Heading style. A JSX fragment can also be provided. |
footer | Sets the footer content. When provided, footer content is wrapped in an HStack by default with the appropriate spacing. If the footer content is wrapped in a layout container (Box , Stack , etc.), it will be rendered as the footer element. |
isClosable | Whether to display the close button. Defaults to true . |
size | Size of the Sheet component; which can either be auto or full . auto makes the bottom variant of the sheet responsive to its content, while full makes the sheet take up the full height of the current viewport. This prop defaults to auto . |
Contents Component
In some cases you man have multiple distinct sections of content to display in the same Sheet. For example, a multi-step dialog where each selection feeds into the next step.
For this, you can use the separate Sheet.Container
and Sheet.Contents
components to separate the sheet from each distinct section of content.
Bottom Variant
As mentioned above, Sheet is responsive by default. To always display the
sheet from the bottom of the screen, set variant
to "bottom"
.
Modal Variant
To display the Sheet as a Modal in desktop form factors, and bottom sheet in
mobile set variant
to "modal"
.
Placement
The Sheet takes a placement
prop that can be used to control the position of
the drawer on desktop. Mobile placement is always from the bottom of the screen.
It takes the following values:
right
- defaultleft
React Native
Sheet is built with Tamagui and provides a bottom sheet interface optimized for iOS and Android platforms.
Usage
import React, { useState } from 'react';import { Sheet } from '@hoverinc/design-system-react-native';const App = () => { const [isOpen, setIsOpen] = useState(false); return ( <> <Sheet isOpen={isOpen} onOpenChange={setIsOpen} header="Add a photo" size="auto" > <Stack gap="$400" padding="$600"> <Heading size="md">Upload your image</Heading> <Body>Choose a photo from your gallery or take a new one.</Body> </Stack> </Sheet> <Button onPress={() => setIsOpen(true)}>Open Sheet</Button> </> );};
Size Variants
The Sheet component supports two size variants:
// Auto size - fits content (default)<Sheet size="auto" header="Auto Size"> <Stack padding="$600"> <Body>This sheet will size to fit its content.</Body> </Stack></Sheet>// Full size - expands to 100% height<Sheet size="full" header="Full Size"> <Stack padding="$600"> <Body>This sheet takes the full screen height.</Body> </Stack></Sheet>
With Footer
You can add a footer to the sheet for actions:
import React, { useState } from 'react';import { Button, Sheet, Stack } from '@hoverinc/design-system-react-native';const App = () => { const [isOpen, setIsOpen] = useState(false); return ( <Sheet isOpen={isOpen} onOpenChange={setIsOpen} header="Confirm Action" footer={ <Stack flexDirection="row" gap="$300" padding="$600"> <Button variant="outline" onPress={() => setIsOpen(false)}> Cancel </Button> <Button onPress={() => setIsOpen(false)}>Confirm</Button> </Stack> } > <Stack padding="$600"> <Body>Are you sure you want to proceed with this action?</Body> </Stack> </Sheet> );};
Controlled vs Uncontrolled
The Sheet component supports both controlled and uncontrolled usage:
// Controlledconst [isOpen, setIsOpen] = useState(false);<Sheet isOpen={isOpen} onOpenChange={setIsOpen}>...</Sheet>// Uncontrolled<Sheet defaultOpen={false}>...</Sheet>