Saltar al contenido principal

Resizable

Grupos de paneles con manijas arrastrables y accesibles por teclado. Layouts horizontales, verticales y anidados.

Files
  • app/layout.tsx
  • app/page.tsx
  • lib/db.ts
Editor
1'use client';
2
3import { Resizable } from '@/components/ui/resizable';
4
5export function Default() {
6 return (
7 <Resizable className="border-border bg-card h-48 w-full max-w-md rounded-lg border">
8 <Resizable.Panel defaultSize={35} minSize={20}>
9 <div className="flex h-full flex-col gap-2 p-4">
10 <span className="text-sm font-medium">Files</span>
11 <ul className="text-muted-foreground flex flex-col gap-1 font-mono text-xs">
12 <li>app/layout.tsx</li>
13 <li>app/page.tsx</li>
14 <li>lib/db.ts</li>
15 </ul>
16 </div>
17 </Resizable.Panel>
18 <Resizable.Handle withHandle />
19 <Resizable.Panel minSize={30}>
20 <div className="flex h-full items-center justify-center p-4">
21 <span className="text-muted-foreground text-sm">Editor</span>
22 </div>
23 </Resizable.Panel>
24 </Resizable>
25 );
26}

Instalación

pnpm dlx nachui add resizable

Anatomía

1import { Resizable } from '@/components/ui/resizable';
1<Resizable direction="horizontal">
2 <Resizable.Panel defaultSize={30} minSize={20}>
3 Sidebar
4 </Resizable.Panel>
5 <Resizable.Handle withHandle />
6 <Resizable.Panel>Contenido</Resizable.Panel>
7</Resizable>
Los tamaños son porcentajes del grupo. Los paneles sin defaultSize se reparten lo que queda. Arrastrar una manija mueve el límite entre los dos paneles vecinos y nunca deja que alguno pase su minSize o maxSize. La manija es un separator con aria-valuenow, recibe foco y se mueve con las flechas, Home y End. Con Shift los pasos son más grandes.

Composición

Resizable
├── Resizable.Panel
├── Resizable.Handle
└── Resizable.Panel
Paneles y manijas deben ser hijos directos del grupo para que pueda numerarlos. Anida otro Resizable dentro de un panel para armar grillas.

Variantes

Predeterminado (Default)

Dos paneles lado a lado con un agarre visible en la manija.
Files
  • app/layout.tsx
  • app/page.tsx
  • lib/db.ts
Editor
1'use client';
2
3import { Resizable } from '@/components/ui/resizable';
4
5export function Default() {
6 return (
7 <Resizable className="border-border bg-card h-48 w-full max-w-md rounded-lg border">
8 <Resizable.Panel defaultSize={35} minSize={20}>
9 <div className="flex h-full flex-col gap-2 p-4">
10 <span className="text-sm font-medium">Files</span>
11 <ul className="text-muted-foreground flex flex-col gap-1 font-mono text-xs">
12 <li>app/layout.tsx</li>
13 <li>app/page.tsx</li>
14 <li>lib/db.ts</li>
15 </ul>
16 </div>
17 </Resizable.Panel>
18 <Resizable.Handle withHandle />
19 <Resizable.Panel minSize={30}>
20 <div className="flex h-full items-center justify-center p-4">
21 <span className="text-muted-foreground text-sm">Editor</span>
22 </div>
23 </Resizable.Panel>
24 </Resizable>
25 );
26}

Vertical

direction="vertical" apila los paneles y la manija pasa a ser una barra horizontal.
Preview

$ pnpm build

Compiled 42 routes in 3.1s

Ready on http://localhost:3000

1'use client';
2
3import { Resizable } from '@/components/ui/resizable';
4
5export function Vertical() {
6 return (
7 <Resizable
8 direction="vertical"
9 className="border-border bg-card h-64 w-full max-w-md rounded-lg border"
10 >
11 <Resizable.Panel defaultSize={60} minSize={25}>
12 <div className="flex h-full items-center justify-center p-4">
13 <span className="text-sm font-medium">Preview</span>
14 </div>
15 </Resizable.Panel>
16 <Resizable.Handle withHandle />
17 <Resizable.Panel minSize={20}>
18 <div className="text-muted-foreground h-full overflow-auto p-4 font-mono text-xs leading-relaxed">
19 <p>$ pnpm build</p>
20 <p>Compiled 42 routes in 3.1s</p>
21 <p>Ready on http://localhost:3000</p>
22 </div>
23 </Resizable.Panel>
24 </Resizable>
25 );
26}

Anidado

Un grupo vertical dentro de un panel de un grupo horizontal.
Sidebar
Editor
Terminal
1'use client';
2
3import { Resizable } from '@/components/ui/resizable';
4
5export function Nested() {
6 return (
7 <Resizable className="border-border bg-card h-64 w-full max-w-md rounded-lg border">
8 <Resizable.Panel defaultSize={28} minSize={15}>
9 <div className="flex h-full items-center justify-center p-4">
10 <span className="text-muted-foreground text-sm">Sidebar</span>
11 </div>
12 </Resizable.Panel>
13 <Resizable.Handle />
14 <Resizable.Panel minSize={30}>
15 <Resizable direction="vertical">
16 <Resizable.Panel defaultSize={65} minSize={30}>
17 <div className="flex h-full items-center justify-center p-4">
18 <span className="text-sm font-medium">Editor</span>
19 </div>
20 </Resizable.Panel>
21 <Resizable.Handle />
22 <Resizable.Panel minSize={15}>
23 <div className="flex h-full items-center justify-center p-4">
24 <span className="text-muted-foreground text-sm">Terminal</span>
25 </div>
26 </Resizable.Panel>
27 </Resizable>
28 </Resizable.Panel>
29 </Resizable>
30 );
31}

Referencia de API

Resizable

PropTipoPor defectoDescripción
direction"horizontal" | "vertical""horizontal"Eje del layout
onLayout(sizes: number[]) => void-Se llama con todos los tamaños después de cada cambio
classNamestring-Clases para el grupo. Dale una altura si es vertical

Resizable.Panel

PropTipoPor defectoDescripción
defaultSizenumber-Tamaño inicial en porcentaje
minSizenumber10Tamaño mínimo en porcentaje
maxSizenumber100Tamaño máximo en porcentaje
classNamestring-Clases CSS adicionales. El contenido se recorta

Resizable.Handle

PropTipoPor defectoDescripción
withHandlebooleanfalseRenderiza un pequeño agarre en el medio
disabledbooleanfalseIgnora puntero y teclado
stepnumber2Porcentaje que mueve cada pulsación de flecha
classNamestring-Clases CSS adicionales
¿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
Ctrl+I