Saltar al contenido

Redimensionable

Paneles divididos que se pueden arrastrar para editores de código, árboles de archivos y diseños de paneles de control, con ese estilo neobrutalista de bordes y sombras.

import {
  ResizableHandle,
  ResizablePanel,

El componente redimensionable divide una región en paneles que el usuario arrastra para repartir el espacio: en horizontal o en vertical, anidables y con tiradores manejables por teclado. Ambas variantes envuelven react-resizable-panels, de bvaughn, y siguen la receta neobrutalista: bordes gruesos, sombras marcadas y tipografía en negrita.

Recurre a este componente cuando:

  • Editores de código y diseños tipo IDE — árbol de archivos, editor y panel de vista previa que cada usuario dimensiona a su gusto.
  • Vistas maestro-detalle — bandeja de entrada e hilo, tabla e inspector de registros, con el divisor donde el usuario lo quiera.
  • Dashboards y consolas — deja que un panel de logs o una terminal le robe altura a la vista principal sin recurrir a un modal.

Acerca de

El componente Resizable se apoya en react-resizable-panels de bvaughn.

Instalación

pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/resizable.json
npx shadcn@latest add https://neobrutalism.com/r/base/resizable.json
yarn dlx shadcn@latest add https://neobrutalism.com/r/base/resizable.json
bunx --bun shadcn@latest add https://neobrutalism.com/r/base/resizable.json

Instala las siguientes dependencias:

pnpm add react-resizable-panels
npm install react-resizable-panels
yarn add react-resizable-panels
bun add react-resizable-panels

Copia y pega el siguiente código en tu proyecto.

components/ui/resizable.tsx
"use client"

import * as ResizablePrimitive from "react-resizable-panels"

import { cn } from "@/lib/utils"

function ResizablePanelGroup({
  className,
  ...props
}: ResizablePrimitive.GroupProps) {
  return (
    <ResizablePrimitive.Group
      data-slot="resizable-panel-group"
      className={cn(
        "flex h-full w-full aria-[orientation=vertical]:flex-col",
        className
      )}
      {...props}
    />
  )
}

function ResizablePanel({ ...props }: ResizablePrimitive.PanelProps) {
  return <ResizablePrimitive.Panel data-slot="resizable-panel" {...props} />
}

function ResizableHandle({
  withHandle,
  className,
  ...props
}: ResizablePrimitive.SeparatorProps & {
  withHandle?: boolean
}) {
  return (
    <ResizablePrimitive.Separator
      data-slot="resizable-handle"
      className={cn(
        "relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary aria-[orientation=horizontal]:h-px aria-[orientation=horizontal]:w-full aria-[orientation=horizontal]:after:left-0 aria-[orientation=horizontal]:after:h-1 aria-[orientation=horizontal]:after:w-full aria-[orientation=horizontal]:after:translate-x-0 aria-[orientation=horizontal]:after:-translate-y-1/2 [&[aria-orientation=horizontal]>div]:rotate-90",
        className
      )}
      {...props}
    >
      {withHandle && (
        <div className="z-10 flex h-6 w-1 shrink-0 rounded-lg bg-border" />
      )}
    </ResizablePrimitive.Separator>
  )
}

export { ResizableHandle, ResizablePanel, ResizablePanelGroup }

Actualiza las rutas de importación según la configuración de tu proyecto.

Uso

import {
  ResizableHandle,
  ResizablePanel,
  ResizablePanelGroup,
} from "@/components/ui/resizable"
<ResizablePanelGroup orientation="horizontal">
  <ResizablePanel>One</ResizablePanel>
  <ResizableHandle />
  <ResizablePanel>Two</ResizablePanel>
</ResizablePanelGroup>

Composición

Usa la siguiente composición para construir un ResizablePanelGroup:

ResizablePanelGroup
├── ResizablePanel
├── ResizableHandle
└── ResizablePanel

Ejemplos

Vertical

Usa orientation="vertical" para un redimensionamiento vertical.

import {
  ResizableHandle,
  ResizablePanel,

Controlador

Usa la prop withHandle en ResizableHandle para mostrar un controlador visible.

import {
  ResizableHandle,
  ResizablePanel,

RTL

Para activar el soporte RTL en Neobrutalism, consulta la guía de configuración de RTL.

"use client"

import * as React from "react"

Accesibilidad

El tirador sigue el patrón Window Splitter de WAI-ARIA: react-resizable-panels renderiza cada tirador como un role="separator" enfocable con aria-valuenow / aria-valuemin / aria-valuemax y aria-controls apuntando al panel que redimensiona, de modo que los lectores de pantalla anuncian la posición de la división a medida que cambia.

Interacciones de teclado:

TeclaAcción
Tab / Shift + TabLleva el foco a un tirador de redimensionamiento o lo saca de él
ArrowLeft / ArrowRightRedimensiona un grupo horizontal un paso
ArrowUp / ArrowDownRedimensiona un grupo vertical un paso
HomeEncoge el panel anterior al tirador hasta su tamaño mínimo
EndAgranda el panel anterior al tirador hasta su tamaño máximo
EnterAlterna un panel plegable entre plegado y expandido

Referencia de API

Consulta la documentación react-resizable-panels.

Changelog

2025-02-02 react-resizable-panels v4

Actualización a react-resizable-panels v4. Consulta las notas de la versión v4.0.0 para ver todos los detalles.

Si usas directamente las primitivas de react-resizable-panels, ten en cuenta los siguientes cambios:

v3v4
PanelGroupGroup
PanelResizeHandleSeparator
direction proporientation prop
defaultSize={50}defaultSize="50%"
onLayoutonLayoutChange
ImperativePanelHandlePanelImperativeHandle
ref prop on PanelpanelRef prop
data-panel-group-directionaria-orientation