Resizable

Adjustable panels separated by draggable handles.

Split a view into panels that resize by drag or keyboard. Built on react-resizable-panels.

One
Two
Three
tsx
import {  ResizableHandle,  ResizablePanel,  ResizablePanelGroup,} from "@/components/ui/resizable"export function Example() {  return (    <ResizablePanelGroup      orientation="horizontal"      className="min-h-[200px] w-full max-w-md rounded-md border border-border"    >      <ResizablePanel defaultSize="25%">        <div className="grid h-full place-items-center">          <span className="text-sm font-medium tracking-wide text-foreground">            One          </span>        </div>      </ResizablePanel>      <ResizableHandle />      <ResizablePanel defaultSize="75%">        <ResizablePanelGroup orientation="vertical">          <ResizablePanel defaultSize="25%">            <div className="grid h-full place-items-center">              <span className="text-sm font-medium tracking-wide text-foreground">                Two              </span>            </div>          </ResizablePanel>          <ResizableHandle />          <ResizablePanel defaultSize="75%">            <div className="grid h-full place-items-center">              <span className="text-sm font-medium tracking-wide text-foreground">                Three              </span>            </div>          </ResizablePanel>        </ResizablePanelGroup>      </ResizablePanel>    </ResizablePanelGroup>  )}

Installation

npx @arctis-sh/@arctis-sh/ui@latest add resizable

Usage

import {  ResizableHandle,  ResizablePanel,  ResizablePanelGroup,} from "@/components/ui/resizable"
<ResizablePanelGroup  orientation="horizontal"  className="min-h-[200px] max-w-md rounded-md border border-border">  <ResizablePanel defaultSize="25%">Sidebar</ResizablePanel>  <ResizableHandle />  <ResizablePanel defaultSize="75%">Main</ResizablePanel></ResizablePanelGroup>

Composition

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

Basic

Nest groups to combine directions. This example uses a horizontal split with a vertical split on the right.

One
Two
Three
tsx
import {  ResizableHandle,  ResizablePanel,  ResizablePanelGroup,} from "@/components/ui/resizable"export function Example() {  return (    <ResizablePanelGroup      orientation="horizontal"      className="min-h-[200px] w-full max-w-md rounded-md border border-border"    >      <ResizablePanel defaultSize="25%">        <div className="grid h-full place-items-center">          <span className="text-sm font-medium tracking-wide text-foreground">            One          </span>        </div>      </ResizablePanel>      <ResizableHandle />      <ResizablePanel defaultSize="75%">        <ResizablePanelGroup orientation="vertical">          <ResizablePanel defaultSize="25%">            <div className="grid h-full place-items-center">              <span className="text-sm font-medium tracking-wide text-foreground">                Two              </span>            </div>          </ResizablePanel>          <ResizableHandle />          <ResizablePanel defaultSize="75%">            <div className="grid h-full place-items-center">              <span className="text-sm font-medium tracking-wide text-foreground">                Three              </span>            </div>          </ResizablePanel>        </ResizablePanelGroup>      </ResizablePanel>    </ResizablePanelGroup>  )}

Vertical

Use orientation="vertical" for stacked panels. Give the group a height, such as min-h-[200px].

Header
Content
tsx
import {  ResizableHandle,  ResizablePanel,  ResizablePanelGroup,} from "@/components/ui/resizable"export function Example() {  return (    <ResizablePanelGroup      orientation="vertical"      className="min-h-[200px] w-full max-w-md rounded-md border border-border"    >      <ResizablePanel defaultSize="25%">        <div className="grid h-full place-items-center">          <span className="text-sm font-medium tracking-wide text-foreground">            Header          </span>        </div>      </ResizablePanel>      <ResizableHandle />      <ResizablePanel defaultSize="75%">        <div className="grid h-full place-items-center">          <span className="text-sm font-medium tracking-wide text-foreground">            Content          </span>        </div>      </ResizablePanel>    </ResizablePanelGroup>  )}

Handle

Pass withHandle on ResizableHandle for a visible grip. The grip rotates automatically in vertical groups.

Sidebar
Content
Header
Content
tsx
import {  ResizableHandle,  ResizablePanel,  ResizablePanelGroup,} from "@/components/ui/resizable"export function Example() {  return (    <div className="grid w-full max-w-md gap-6">      <ResizablePanelGroup        orientation="horizontal"        className="min-h-[200px] rounded-md border border-border"      >        <ResizablePanel defaultSize="25%">          <div className="grid h-full place-items-center">            <span className="text-sm font-medium tracking-wide text-foreground">              Sidebar            </span>          </div>        </ResizablePanel>        <ResizableHandle withHandle />        <ResizablePanel defaultSize="75%">          <div className="grid h-full place-items-center">            <span className="text-sm font-medium tracking-wide text-foreground">              Content            </span>          </div>        </ResizablePanel>      </ResizablePanelGroup>      <ResizablePanelGroup        orientation="vertical"        className="min-h-[200px] rounded-md border border-border"      >        <ResizablePanel defaultSize="35%">          <div className="grid h-full place-items-center">            <span className="text-sm font-medium tracking-wide text-foreground">              Header            </span>          </div>        </ResizablePanel>        <ResizableHandle withHandle />        <ResizablePanel defaultSize="65%">          <div className="grid h-full place-items-center">            <span className="text-sm font-medium tracking-wide text-foreground">              Content            </span>          </div>        </ResizablePanel>      </ResizablePanelGroup>    </div>  )}

Constraints

Use minSize and maxSize to set resize limits. Percentage strings, including unitless strings like "50", are relative to the group; numbers are pixels.

20%–50%
Remaining
tsx
import {  ResizableHandle,  ResizablePanel,  ResizablePanelGroup,} from "@/components/ui/resizable"export function Example() {  return (    <ResizablePanelGroup      orientation="horizontal"      className="min-h-[200px] w-full max-w-md rounded-md border border-border"    >      <ResizablePanel        defaultSize="30%"        minSize="20%"        maxSize="50%"      >        <div className="grid h-full place-items-center">          <span className="text-sm font-medium tracking-wide text-foreground">            20%–50%          </span>        </div>      </ResizablePanel>      <ResizableHandle withHandle />      <ResizablePanel minSize="30%">        <div className="grid h-full place-items-center">          <span className="text-sm font-medium tracking-wide text-foreground">            Remaining          </span>        </div>      </ResizablePanel>    </ResizablePanelGroup>  )}

API Reference

ResizablePanelGroup

PropTypeDefault
orientation"horizontal" | "vertical""horizontal"
resizeTargetMinimumSize{ coarse: number; fine: number }{ coarse: 8, fine: 4 }
classNamestring
<ResizablePanelGroup orientation="horizontal">  <ResizablePanel defaultSize="50%">A</ResizablePanel>  <ResizableHandle />  <ResizablePanel defaultSize="50%">B</ResizablePanel></ResizablePanelGroup>

ResizablePanel

Size props accept "25%", unitless percentage strings, or pixel numbers.

PropTypeDefault
defaultSizestring | number
minSizestring | number
maxSizestring | number
collapsibleboolean
collapsedSizestring | number0%
disabledboolean
classNamestring
<ResizablePanel defaultSize="50%">A</ResizablePanel>

ResizableHandle

PropTypeDefault
withHandleboolean
disabledboolean
disableDoubleClickboolean
classNamestring

See the react-resizable-panels docs for the full primitive API.

<ResizableHandle />