Saltar al contenido principal

Drawer

Contexto lateral. Preservar estado de página. UX móvil impecable.

default.tsx
1'use client';
2
3import { Button } from '@/components/ui/button';
4import { Drawer } from '@/components/ui/drawer';
5
6const notifications = [
7 { from: 'Jane', message: 'Your report is ready for download.' },
8 { from: 'System Alert', message: 'Scheduled maintenance at 3:00 AM UTC.' },
9 { from: 'John', message: 'Please review the updated project plan.' },
10] as const;
11
12export function Default() {
13 return (
14 <Drawer>
15 <Drawer.Trigger variant="outline">Open Drawer</Drawer.Trigger>
16 <Drawer.Content side="right">
17 <div className="flex flex-col gap-4">
18 <h2 className="text-lg font-semibold">Notifications</h2>
19 <p className="text-muted-foreground text-sm">
20 You have 3 new messages and 1 system alert. Review them below.
21 </p>
22
23 <div className="space-y-2 text-sm">
24 {notifications.map((n) => (
25 <div key={n.from} className="bg-accent/10 rounded-md border p-3">
26 <strong>
27 {n.from === 'System Alert' ? 'System Alert:' : `Message from ${n.from}:`}
28 </strong>{' '}
29 {n.message}
30 </div>
31 ))}
32 </div>
33
34 <div className="mt-4 flex justify-end gap-2">
35 <Drawer.Close>Dismiss All</Drawer.Close>
36 <Button>View Details</Button>
37 </div>
38 </div>
39 </Drawer.Content>
40 </Drawer>
41 );
42}

Instalación

pnpm dlx nachui add drawer

Anatomía

1import { Drawer } from '@/components/ui/drawer';
1<Drawer>
2 <Drawer.Trigger>
3 <Button>Abrir Drawer</Button>
4 </Drawer.Trigger>
5 <Drawer.Content>
6 <Drawer.Header>
7 <Drawer.Title>Título del Drawer</Drawer.Title>
8 <Drawer.Description>Este es un componente drawer.</Drawer.Description>
9 </Drawer.Header>
10 <div>Tu contenido aquí</div>
11 </Drawer.Content>
12</Drawer>

Características

  • Múltiples lados - Desliza desde abajo, arriba, izquierda o derecha
  • Arrastrar para cerrar - Desliza para descartar en cualquier dirección
  • Responsivo - Se adapta a diferentes tamaños de pantalla
  • Animaciones suaves - Transiciones de deslizamiento basadas en resorte
  • Trampa de foco - La navegación por teclado se mantiene dentro del drawer
  • Bloqueo de scroll - Evita el desplazamiento del fondo cuando está abierto

Variantes

Posiciones (Positions)

positions.tsx
1'use client';
2
3import { Drawer } from '@/components/ui/drawer';
4
5const DRAWER_SIDES = ['top', 'right', 'bottom', 'left'] as const;
6
7export function Positions() {
8 return (
9 <div className="grid grid-cols-2 gap-4">
10 {DRAWER_SIDES.map((side) => (
11 <Drawer key={side}>
12 <Drawer.Trigger className="w-full capitalize" variant="outline">
13 {side}
14 </Drawer.Trigger>
15 <Drawer.Content side={side}>
16 <Drawer.Header>
17 <Drawer.Title>Edit profile</Drawer.Title>
18 <Drawer.Description>
19 Make changes to your profile here. Click save when you're done.
20 </Drawer.Description>
21 </Drawer.Header>
22 <div className="py-4">
23 <p className="text-muted-foreground text-sm">
24 This drawer is appearing from the {side}.
25 </p>
26 </div>
27 <div className="flex justify-end">
28 <Drawer.Close>Save changes</Drawer.Close>
29 </div>
30 </Drawer.Content>
31 </Drawer>
32 ))}
33 </div>
34 );
35}

Formulario (Form)

form.tsx
1'use client';
2
3import { Drawer } from '@/components/ui/drawer';
4import { Input } from '@/components/ui/input';
5import { Label } from '@/components/ui/label';
6
7const fields = [
8 { id: 'name', label: 'Name', defaultValue: 'Ignacio Figueroa', type: 'text' },
9 { id: 'username', label: 'Username', defaultValue: '@figueroaignacio', type: 'text' },
10] as const;
11
12export function Form() {
13 return (
14 <Drawer>
15 <Drawer.Trigger variant="outline">Edit Profile</Drawer.Trigger>
16 <Drawer.Content side="bottom">
17 <Drawer.Header>
18 <Drawer.Title>Edit profile</Drawer.Title>
19 <Drawer.Description>
20 Make changes to your profile here. Click save when you&apos;re done.
21 </Drawer.Description>
22 </Drawer.Header>
23 <div className="grid gap-4 py-4">
24 {fields.map((field) => (
25 <div key={field.id} className="grid grid-cols-4 items-center gap-4">
26 <Label htmlFor={field.id} className="text-right">
27 {field.label}
28 </Label>
29 <Input
30 id={field.id}
31 type={field.type}
32 defaultValue={field.defaultValue}
33 className="col-span-3"
34 />
35 </div>
36 ))}
37 </div>
38 <div className="flex justify-end">
39 <Drawer.Close>Save changes</Drawer.Close>
40 </div>
41 </Drawer.Content>
42 </Drawer>
43 );
44}

Referencia de API

Drawer

PropTipoPor defectoDescripción
childrenReactNode-Activador y contenido del drawer
defaultOpenbooleanfalseEstado abierto inicial
openboolean-Estado abierto controlado
onOpenChange(open: boolean) => void-Callback cuando cambia el estado

Drawer.Trigger

PropTipoPor defectoDescripción
childrenReactNode-Contenido del botón activador
classNamestring-Clases CSS adicionales
variantButtonProps['variant']-Variante del botón
sizeButtonProps['size']-Tamaño del botón

Drawer.Content

PropTipoPor defectoDescripción
side'bottom' | 'top' | 'left' | 'right''bottom'Lado desde el que deslizar
showDragHandlebooleantrueMostrar asa de arrastre para cerrar
classNamestring-Clases CSS adicionales

Drawer.Header

PropTipoPor defectoDescripción
classNamestring-Clases CSS adicionales

Drawer.Title

PropTipoPor defectoDescripción
classNamestring-Clases CSS adicionales

Drawer.Description

PropTipoPor defectoDescripción
classNamestring-Clases CSS adicionales

Drawer.Close

PropTipoPor defectoDescripción
childrenReactNode-Contenido clicable
¿Encontraste algo que mejorar?

¿Notaste un error, tipografía o detalle faltante en esta página? Ayúdanos a mejorar la documentación abriendo un issue en GitHub.

Crear un Issue