Zaidan

Command Palette

Search for a command to run...

GitHub43

Resizable

Accessible resizable panel groups and layouts with keyboard support.

Installation

CLI

Manual

Install the following dependencies:

Copy and paste the following code into your project.

import {
Handle,
type HandleProps,
Panel,
type PanelProps,
Root,
type RootProps,
} from "@corvu/resizable";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { ComponentProps, ValidComponent } from "solid-js";
import { Show, splitProps } from "solid-js";
import { cn } from "~/lib/utils";
type ResizablePanelGroupProps<T extends ValidComponent = "div"> = PolymorphicProps<
T,
RootProps<T>
> &
Pick<ComponentProps<T>, "class">;
const ResizablePanelGroup = <T extends ValidComponent = "div">(
props: ResizablePanelGroupProps<T>,
) => {
const [local, others] = splitProps(props as ResizablePanelGroupProps, ["class"]);
return (
<Root
class={cn(
"z-resizable-panel-group flex h-full w-full data-[orientation=vertical]:flex-col",
local.class,
)}
data-slot="resizable-panel-group"
{...others}
/>
);
};
type ResizablePanelProps<T extends ValidComponent = "div"> = PolymorphicProps<T, PanelProps<T>>;
const ResizablePanel = <T extends ValidComponent = "div">(props: ResizablePanelProps<T>) => {
return <Panel data-slot="resizable-panel" {...props} />;
};
type ResizableHandleProps<T extends ValidComponent = "button"> = PolymorphicProps<
T,
HandleProps<T>
> &
Pick<ComponentProps<T>, "class"> & {
withHandle?: boolean;
};
const ResizableHandle = <T extends ValidComponent = "button">(props: ResizableHandleProps<T>) => {
const [local, others] = splitProps(props as ResizableHandleProps, ["class", "withHandle"]);
return (
<Handle
class={cn(
"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",
local.class,
)}
data-slot="resizable-handle"
{...others}
>
<Show when={local.withHandle}>
<div class="z-10 z-resizable-handle-icon flex shrink-0" />
</Show>
</Handle>
);
};
export { ResizablePanelGroup, ResizablePanel, ResizableHandle };

Examples

Here are the source code of all the examples from the preview page:

Horizontal

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>

Vertical

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>

With Handle

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>

Nested

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>

Controlled

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>