kosori

Sheet

A flyout that opens from the side of the screen, based on the dialog component.

Sheet wraps the Base UI Dialog primitive, composes Button and its scrollable panel through Base UI's render prop, and marks each part with data-slot. It is a client component: it opens from any edge, uses a portal, manages focus, and animates with tw-animate-css.

Installation

npx shadcn@latest add @kosori/sheet

Example

sheet-example.tsx
sheet-example
import { Button } from '@/components/ui/button';
import {
  Sheet,
  SheetClose,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetPanel,
  SheetPopup,
  SheetTitle,
  SheetTrigger,
} from '@/components/ui/sheet';

const SheetExample = () => (
  <div className='flex flex-wrap gap-2'>
    <Sheet>
      <SheetTrigger render={<Button variant='outline' />}>
        Open Right
      </SheetTrigger>
      <SheetPopup>
        <SheetHeader>
          <SheetTitle>Right</SheetTitle>
          <SheetDescription>Right side of the screen.</SheetDescription>
        </SheetHeader>
        <SheetPanel>
          <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
            ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat. Duis aute irure dolor in
            reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
            pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
            culpa qui officia deserunt mollit anim id est laborum.
          </p>
        </SheetPanel>
        <SheetFooter>
          <SheetClose render={<Button variant='ghost' />}>Close</SheetClose>
        </SheetFooter>
      </SheetPopup>
    </Sheet>
    <Sheet>
      <SheetTrigger render={<Button variant='outline' />}>
        Open Left
      </SheetTrigger>
      <SheetPopup side='left'>
        <SheetHeader>
          <SheetTitle>Left</SheetTitle>
          <SheetDescription>Left side of the screen.</SheetDescription>
        </SheetHeader>
        <SheetPanel>
          <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
            ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat. Duis aute irure dolor in
            reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
            pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
            culpa qui officia deserunt mollit anim id est laborum.
          </p>
        </SheetPanel>
        <SheetFooter>
          <SheetClose render={<Button variant='ghost' />}>Close</SheetClose>
        </SheetFooter>
      </SheetPopup>
    </Sheet>
    <Sheet>
      <SheetTrigger render={<Button variant='outline' />}>
        Open Top
      </SheetTrigger>
      <SheetPopup side='top'>
        <SheetHeader>
          <SheetTitle>Top</SheetTitle>
          <SheetDescription>Top of the screen.</SheetDescription>
        </SheetHeader>
        <SheetPanel>
          <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
            ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat. Duis aute irure dolor in
            reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
            pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
            culpa qui officia deserunt mollit anim id est laborum.
          </p>
        </SheetPanel>
        <SheetFooter>
          <SheetClose render={<Button variant='ghost' />}>Close</SheetClose>
        </SheetFooter>
      </SheetPopup>
    </Sheet>
    <Sheet>
      <SheetTrigger render={<Button variant='outline' />}>
        Open Bottom
      </SheetTrigger>
      <SheetPopup side='bottom'>
        <SheetHeader>
          <SheetTitle>Bottom</SheetTitle>
          <SheetDescription>Bottom of the screen.</SheetDescription>
        </SheetHeader>
        <SheetPanel>
          <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
            ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat. Duis aute irure dolor in
            reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
            pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
            culpa qui officia deserunt mollit anim id est laborum.
          </p>
        </SheetPanel>
        <SheetFooter>
          <SheetClose render={<Button variant='ghost' />}>Close</SheetClose>
        </SheetFooter>
      </SheetPopup>
    </Sheet>
  </div>
);

export default SheetExample;

Usage

import { Button } from '@/components/ui/button';
import {
  Sheet,
  SheetClose,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetPopup,
  SheetTitle,
  SheetTrigger,
} from '@/components/ui/sheet';

const SheetDemo = () => (
  <Sheet>
    <SheetTrigger render={<Button variant='outline' />}>
      Open sheet
    </SheetTrigger>
    <SheetPopup>
      <SheetHeader>
        <SheetTitle>Edit profile</SheetTitle>
        <SheetDescription>Make changes to your profile.</SheetDescription>
      </SheetHeader>
      <SheetFooter>
        <SheetClose render={<Button variant='outline' />}>Cancel</SheetClose>
        <Button>Save changes</Button>
      </SheetFooter>
    </SheetPopup>
  </Sheet>
);

On this page