Search for a command to run...
Accessible resizable panel groups and layouts with keyboard support.
npx shadcn@latest add @zaidan/resizablepnpx shadcn add @zaidan/resizableyarn dlx shadcn@latest add @zaidan/resizablebunx shadcn@latest add @zaidan/resizableInstall the following dependencies:
npm i @corvu/resizablepnpm add @corvu/resizableyarn add @corvu/resizablebun add @corvu/resizableCopy and paste the following code into your project.
1import {2 Handle,3 type HandleProps,4 Panel,5 type PanelProps,6 Root,7 type RootProps,8} from "@corvu/resizable";9import type { PolymorphicProps } from "@kobalte/core/polymorphic";10import type { ComponentProps, ValidComponent } from "solid-js";11import { Show, splitProps } from "solid-js";12
13import { cn } from "~/lib/utils";14
15type ResizablePanelGroupProps<T extends ValidComponent = "div"> = PolymorphicProps<16 T,17 RootProps<T>18> &19 Pick<ComponentProps<T>, "class">;20
21const ResizablePanelGroup = <T extends ValidComponent = "div">(22 props: ResizablePanelGroupProps<T>,23) => {24 const [local, others] = splitProps(props as ResizablePanelGroupProps, ["class"]);25 return (26 <Root27 class={cn(28 "z-resizable-panel-group flex h-full w-full data-[orientation=vertical]:flex-col",29 local.class,30 )}31 data-slot="resizable-panel-group"32 {...others}33 />34 );35};36
37type ResizablePanelProps<T extends ValidComponent = "div"> = PolymorphicProps<T, PanelProps<T>>;38
39const ResizablePanel = <T extends ValidComponent = "div">(props: ResizablePanelProps<T>) => {40 return <Panel data-slot="resizable-panel" {...props} />;41};42
43type ResizableHandleProps<T extends ValidComponent = "button"> = PolymorphicProps<44 T,45 HandleProps<T>46> &47 Pick<ComponentProps<T>, "class"> & {48 withHandle?: boolean;49 };50
51const ResizableHandle = <T extends ValidComponent = "button">(props: ResizableHandleProps<T>) => {52 const [local, others] = splitProps(props as ResizableHandleProps, ["class", "withHandle"]);53 return (54 <Handle55 class={cn(56 "relative z-resizable-handle 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-hidden focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[orientation=vertical]:h-px data-[orientation=vertical]:w-full data-[orientation=vertical]:after:left-0 data-[orientation=vertical]:after:h-1 data-[orientation=vertical]:after:w-full data-[orientation=vertical]:after:translate-x-0 data-[orientation=vertical]:after:-translate-y-1/2 [&[data-orientation=vertical]>div]:rotate-90",57 local.class,58 )}59 data-slot="resizable-handle"60 {...others}61 >62 <Show when={local.withHandle}>63 <div class="z-10 z-resizable-handle-icon flex shrink-0" />64 </Show>65 </Handle>66 );67};68
69export { ResizablePanelGroup, ResizablePanel, ResizableHandle };Here are the source code of all the examples from the preview page:
import { ResizableHandle, ResizablePanel, ResizablePanelGroup,} from "~/components/ui/resizable"; </ExampleWrapper> );}
function ResizableHorizontal() { return ( <Example title="Horizontal"> <ResizablePanelGroup orientation="horizontal" class="min-h-[200px] rounded-lg border"> <ResizablePanel initialSize={0.25}> <div class="flex h-full items-center justify-center p-6"> <span class="font-semibold">Sidebar</span> </div> </ResizablePanel> <ResizableHandle /> <ResizablePanel initialSize={0.75}> <div class="flex h-full items-center justify-center p-6"> <span class="font-semibold">Content</span> </div> </ResizablePanel>Use the orientation prop to set the orientation of the resizable panels.
import { ResizableHandle, ResizablePanel, ResizablePanelGroup,} from "~/components/ui/resizable"; </Example> );}
function ResizableVertical() { return ( <Example title="Vertical"> <ResizablePanelGroup orientation="vertical" class="min-h-[200px] rounded-lg border"> <ResizablePanel initialSize={0.25}> <div class="flex h-full items-center justify-center p-6"> <span class="font-semibold">Header</span> </div> </ResizablePanel> <ResizableHandle /> <ResizablePanel initialSize={0.75}> <div class="flex h-full items-center justify-center p-6"> <span class="font-semibold">Content</span> </div> </ResizablePanel>You can set or hide the handle by using the withHandle prop on the ResizableHandle component.
import { ResizableHandle, ResizablePanel, ResizablePanelGroup,} from "~/components/ui/resizable"; </Example> );}
function ResizableWithHandle() { return ( <Example title="With Handle"> <ResizablePanelGroup orientation="horizontal" class="min-h-[200px] rounded-lg border"> <ResizablePanel initialSize={0.25}> <div class="flex h-full items-center justify-center p-6"> <span class="font-semibold">Sidebar</span> </div> </ResizablePanel> <ResizableHandle withHandle /> <ResizablePanel initialSize={0.75}> <div class="flex h-full items-center justify-center p-6"> <span class="font-semibold">Content</span> </div> </ResizablePanel>You can nest ResizablePanelGroup components to create more complex layouts.
import { ResizableHandle, ResizablePanel, ResizablePanelGroup,} from "~/components/ui/resizable"; </Example> );}
function ResizableNested() { return ( <Example title="Nested"> <ResizablePanelGroup orientation="horizontal" class="rounded-lg border"> <ResizablePanel initialSize={0.5}> <div class="flex h-[200px] items-center justify-center p-6"> <span class="font-semibold">One</span> </div> </ResizablePanel> <ResizableHandle /> <ResizablePanel initialSize={0.5}> <ResizablePanelGroup orientation="vertical"> <ResizablePanel initialSize={0.25}> <div class="flex h-full items-center justify-center p-6"> <span class="font-semibold">Two</span> </div> </ResizablePanel> <ResizableHandle /> <ResizablePanel initialSize={0.75}> <div class="flex h-full items-center justify-center p-6"> <span class="font-semibold">Three</span> </div> </ResizablePanel> </ResizablePanelGroup> </ResizablePanel>Use the sizes and onSizesChange props to control the panel sizes.
import { createSignal } from "solid-js";import { ResizableHandle, ResizablePanel, ResizablePanelGroup,} from "~/components/ui/resizable"; </Example> );}
function ResizableControlled() { const [sizes, setSizes] = createSignal([0.3, 0.7]);
return ( <Example title="Controlled"> <ResizablePanelGroup orientation="horizontal" class="min-h-[200px] rounded-lg border" sizes={sizes()} onSizesChange={(newSizes) => { setSizes(newSizes); }} > <ResizablePanel initialSize={0.3} minSize={0.2}> <div class="flex h-full flex-col items-center justify-center gap-2 p-6"> <span class="font-semibold">{Math.round((sizes()[0] ?? 0.3) * 100)}%</span> </div> </ResizablePanel> <ResizableHandle /> <ResizablePanel initialSize={0.7} minSize={0.3}> <div class="flex h-full flex-col items-center justify-center gap-2 p-6"> <span class="font-semibold">{Math.round((sizes()[1] ?? 0.7) * 100)}%</span> </div> </ResizablePanel>