Build websites 10x faster with HextaUI Blocks — Learn more
UI/UI

Resizable

Accessible resizable panel groups and layouts.

One
Two
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.

components/ui/resizable.tsx
"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

One
Two
<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

Header
Content
<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}.

Sidebar
Content
<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

Left
Center
Right
<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.

Sidebar
Main Content
Footer
<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}.

Collapsible
Main
<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

PropTypeDefault
autoSaveId?
string
undefined
className?
string
undefined
direction?
"horizontal" | "vertical"
"horizontal"

ResizablePanel

PropTypeDefault
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

PropTypeDefault
className?
string
undefined
disabled?
boolean
false
withHandle?
boolean
true
Edit on GitHub

Last updated on