UI/UI
Resizable
Accessible resizable panel groups and layouts.
import {
ResizablePanelGroup,
ResizablePanel,
ResizableHandle,
} from "@/components/ui/Resizable";
<ResizablePanelGroup
direction="horizontal"
className="max-w-md rounded-lg border"
>
<ResizablePanel defaultSize={50}>
<div className="flex h-[200px] items-center justify-center p-6">
<span className="font-semibold">One</span>
</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={50}>
<div className="flex h-[200px] items-center justify-center p-6">
<span className="font-semibold">Two</span>
</div>
</ResizablePanel>
</ResizablePanelGroup>
Installation
Install the required dependencies:
npm install react-resizable-panels lucide-react
pnpm add react-resizable-panels lucide-react
yarn add react-resizable-panels lucide-react
bun add react-resizable-panels lucide-react
Copy and paste the following code into your project.
"use client";
import * as React from "react";
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
import { cva, type VariantProps } from "class-variance-authority";
import { GripVertical } from "lucide-react";
import { cn } from "@/lib/utils";
const resizablePanelVariants = cva("relative", {
variants: {
border: {
none: "",
left: "border-l",
right: "border-r",
top: "border-t",
bottom: "border-b",
all: "border",
vertical: "border-l border-r",
horizontal: "border-t border-b",
},
},
defaultVariants: {
border: "none",
},
});
const ResizablePanelGroup = React.forwardRef<
React.ElementRef<typeof PanelGroup>,
React.ComponentPropsWithoutRef<typeof PanelGroup>
>(({ className, ...props }, ref) => (
<PanelGroup
ref={ref}
className={cn(
"flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
className,
)}
{...props}
/>
));
ResizablePanelGroup.displayName = "ResizablePanelGroup";
interface ResizablePanelProps
extends React.ComponentPropsWithoutRef<typeof Panel>,
VariantProps<typeof resizablePanelVariants> {}
const ResizablePanel = React.forwardRef<
React.ElementRef<typeof Panel>,
ResizablePanelProps
>(({ className, border, ...props }, ref) => (
<Panel
ref={ref}
className={cn(resizablePanelVariants({ border }), className)}
{...props}
/>
));
ResizablePanel.displayName = "ResizablePanel";
const ResizableHandle = ({
withHandle = true,
className,
...props
}: React.ComponentProps<typeof PanelResizeHandle> & {
withHandle?: boolean;
}) => (
<PanelResizeHandle
className={cn(
"relative flex w-px items-center justify-center bg-[hsl(var(--hu-border))] after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
className,
)}
{...props}
>
{withHandle && (
<div className="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-[hsl(var(--hu-border))]">
<GripVertical className="h-2.5 w-2.5" />
</div>
)}
</PanelResizeHandle>
);
export {
ResizablePanelGroup,
ResizablePanel,
ResizableHandle,
resizablePanelVariants,
};
npx hextaui@latest add resizable
pnpm dlx hextaui@latest add resizable
yarn dlx hextaui@latest add resizable
bun x hextaui@latest add resizable
Usage
import {
ResizablePanelGroup,
ResizablePanel,
ResizableHandle,
} from "@/components/ui/Resizable";
<ResizablePanelGroup direction="horizontal">
<ResizablePanel defaultSize={50}>
<div>Panel content</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={50}>
<div>Panel content</div>
</ResizablePanel>
</ResizablePanelGroup>
Examples
Default
<ResizablePanelGroup
direction="horizontal"
className="max-w-md rounded-lg border"
>
<ResizablePanel defaultSize={50} border="right">
<div className="flex h-[200px] items-center justify-center p-6">
<span className="font-semibold">One</span>
</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={50}>
<div className="flex h-[200px] items-center justify-center p-6">
<span className="font-semibold">Two</span>
</div>
</ResizablePanel>
</ResizablePanelGroup>
Vertical
<ResizablePanelGroup
direction="vertical"
className="min-h-[200px] max-w-md rounded-lg border"
>
<ResizablePanel defaultSize={25} border="bottom">
<div className="flex h-full items-center justify-center p-6">
<span className="font-semibold">Header</span>
</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={75}>
<div className="flex h-full items-center justify-center p-6">
<span className="font-semibold">Content</span>
</div>
</ResizablePanel>
</ResizablePanelGroup>
Handle
You can hide the handle grip icon by setting withHandle={false}
.
<ResizablePanelGroup
direction="horizontal"
className="min-h-[200px] max-w-md rounded-lg border"
>
<ResizablePanel defaultSize={25} border="right">
<div className="flex h-full items-center justify-center p-6">
<span className="font-semibold">Sidebar</span>
</div>
</ResizablePanel>
<ResizableHandle withHandle={false} />
<ResizablePanel defaultSize={75}>
<div className="flex h-full items-center justify-center p-6">
<span className="font-semibold">Content</span>
</div>
</ResizablePanel>
</ResizablePanelGroup>
Three Panel
<ResizablePanelGroup
direction="horizontal"
className="min-h-[200px] max-w-lg rounded-lg border"
>
<ResizablePanel defaultSize={25} minSize={15} border="right">
<div className="flex h-full items-center justify-center p-4">
<span className="font-semibold text-sm">Left</span>
</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={50} minSize={30} border="top">
<div className="flex h-full items-center justify-center p-4">
<span className="font-semibold text-sm">Center</span>
</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={25} minSize={15}>
<div className="flex h-full items-center justify-center p-4">
<span className="font-semibold text-sm">Right</span>
</div>
</ResizablePanel>
</ResizablePanelGroup>
Nested
Create complex layouts by nesting resizable panel groups within each other.
<ResizablePanelGroup
direction="horizontal"
className="min-h-[300px] max-w-lg rounded-lg border"
>
<ResizablePanel defaultSize={25} minSize={20} border="right">
<div className="flex h-full items-center justify-center p-4">
<span className="font-semibold text-sm">Sidebar</span>
</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={75} border="top">
<ResizablePanelGroup direction="vertical">
<ResizablePanel defaultSize={60} minSize={40} border="bottom">
<div className="flex h-full items-center justify-center p-4">
<span className="font-semibold text-sm">Main Content</span>
</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={40} minSize={20}>
<div className="flex h-full items-center justify-center p-4">
<span className="font-semibold text-sm">Footer</span>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>
</ResizablePanelGroup>
Collapsible
Panels can be made collapsible by setting minSize={0}
and collapsible={true}
.
<ResizablePanelGroup
direction="horizontal"
className="min-h-[200px] max-w-md rounded-lg border"
>
<ResizablePanel defaultSize={30} minSize={0} collapsible border="right">
<div className="flex h-full items-center justify-center p-4">
<span className="font-semibold text-sm">Collapsible</span>
</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={70} minSize={50}>
<div className="flex h-full items-center justify-center p-4">
<span className="font-semibold text-sm">Main</span>
</div>
</ResizablePanel>
</ResizablePanelGroup>
Props
ResizablePanelGroup
Prop | Type | Default |
---|---|---|
autoSaveId? | string | undefined |
className? | string | undefined |
direction? | "horizontal" | "vertical" | "horizontal" |
ResizablePanel
Prop | Type | Default |
---|---|---|
className? | string | undefined |
border? | "none" | "left" | "right" | "top" | "bottom" | "all" | "vertical" | "horizontal" | "none" |
collapsible? | boolean | false |
maxSize? | number | undefined |
minSize? | number | undefined |
defaultSize? | number | undefined |
ResizableHandle
Prop | Type | Default |
---|---|---|
className? | string | undefined |
disabled? | boolean | false |
withHandle? | boolean | true |
Edit on GitHub
Last updated on