A component for building resizable panel groups and layouts.
Installation
npx shadcn@latest add @caprice/resizablenpm install @caprice-ui/reactInstall the following dependencies:
npm install @base-ui/react lucide-reactAdd a cn helper
import { defineConfig, type VariantProps } from 'cva';import { twMerge } from 'tailwind-merge';export const { compose, cva, cx: cn,} = defineConfig({ hooks: { onComplete: (className: string) => twMerge(className), },});export type { VariantProps };Copy and paste the following code into your project.
'use client';import { GripVerticalIcon } from 'lucide-react';import type * as React from 'react';import * as ResizablePrimitive from 'react-resizable-panels';import { cn } from '@/lib/utils';/** * A Group wraps a set of resizable Panel components. * Group content can be resized _horizontally_ or _vertically_. * * Group elements always include the following attributes: * * ```html * <div data-group data-testid="group-id-prop" id="group-id-prop"> * ``` * * Documentation: [Caprice UI Resizable](https://caprice-ui.com/docs/components/resizable) * * API Reference: [React Resizable Panels](https://react-resizable-panels.com/docs/group) */export namespace Group { export type Props = React.ComponentProps<typeof ResizablePrimitive.Group>;}export function Group({ ...props }: Group.Props) { return ( <ResizablePrimitive.Group className={cn('size-full', props.className)} data-slot="resizable-group" {...props} /> );}/** * A Panel wraps resizable content and can be configured with min/max size constraints and collapsible behavior. * * Panel size props can be specified using the following CSS units: * - Pixels (default if value is of type `number`) * - Percentages (default if value is of type `string`) * - Font sizes (em, rem) * - Viewport sizes (vh, vw) * * Panel elements always include the following attributes: * * ```html * <div data-panel data-testid="panel-id-prop" id="panel-id-prop"> * ``` * * Documentation: [Caprice UI Resizable](https://caprice-ui.com/docs/components/resizable) * * API Reference: [React Resizable Panels](https://react-resizable-panels.com/docs/panel) */export namespace Panel { export type Props = React.ComponentProps<typeof ResizablePrimitive.Panel>;}export function Panel({ ...props }: Panel.Props) { return <ResizablePrimitive.Panel data-slot="resizable-panel" {...props} />;}/** * Separators are not _required_ but they are _recommended_ as they improve keyboard accessibility. * * Separators should be rendered as the direct child of a Group component. * * Separator elements always include the following attributes: * * ```html * <div data-separator data-testid="separator-id-prop" id="separator-id-prop" role="separator"> * ``` * * Documentation: [Caprice UI Resizable](https://caprice-ui.com/docs/components/resizable) * * API Reference: [React Resizable Panels](https://react-resizable-panels.com/docs/separator) */export namespace Separator { export type Props = React.ComponentProps<typeof ResizablePrimitive.Separator> & { /** * Whether to show the handle. * * @default false */ withHandle?: boolean; };}export function Separator({ withHandle, className, ...props }: Separator.Props) { return ( <ResizablePrimitive.Separator 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-hidden focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 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 )} data-slot="resizable-separator" {...props} > {withHandle && ( <div className="z-10 flex h-4 w-3 items-center justify-center rounded-xs border bg-border"> <GripVerticalIcon className="size-2.5" /> </div> )} </ResizablePrimitive.Separator> );}Update the import paths to match your project setup.
Usage
import * as Resizable from "@/components/caprice-ui/resizable"import * as Resizable from "@caprice-ui/react/resizable"Examples
Vertical
Handle
Nested Group
Collapsible
Differences with shadcn/ui
If you're familiar with shadcn/ui, most patterns will carry over. This guide points out the differences so you can start using Caprice UI without surprises.
Key changes
| Feature | shadcn/ui | Caprice UI |
|---|---|---|
| Import style | Individual named imports | Namespace import (* as Resizable) |
| Group component | ResizablePanelGroup | Resizable.Group |
| Panel component | ResizablePanel | Resizable.Panel |
| Handle component | ResizableHandle | Resizable.Separator |
| Orientation prop | direction | orientation |
| Orientation detection | data-[panel-group-direction=vertical] | aria-[orientation=horizontal] |
| Data slot (group) | resizable-panel-group | resizable-group |
| Data slot (handle) | resizable-handle | resizable-separator |
| Type exports | Regular types | Namespace types (Group.Props, Panel.Props, Separator.Props) |
The component is renamed from "Handle" to "Separator" to align with the ARIA separator role, which is the semantic role used by react-resizable-panels for keyboard accessibility.
Comparison Example
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@/components/ui/resizable';
<ResizablePanelGroup direction="horizontal">
<ResizablePanel defaultSize={25}>Sidebar</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={75}>Content</ResizablePanel>
</ResizablePanelGroup>import * as Resizable from '@/components/caprice-ui/resizable';
<Resizable.Group orientation="horizontal">
<Resizable.Panel defaultSize={25}>Sidebar</Resizable.Panel>
<Resizable.Separator withHandle />
<Resizable.Panel defaultSize={75}>Content</Resizable.Panel>
</Resizable.Group>Accessibility
- Keyboard navigation: Separators can be focused and resized using arrow keys, following the ARIA separator pattern.
- ARIA orientation: The component uses
aria-orientationto communicate the resize direction to assistive technologies. - Focus indicator: Separators display a visible focus ring when focused via keyboard.