Purbayan Pramanik

Snap Panels

Component

About

A resizable split-panel layout whose divider magnetically leans toward configured snap points and can be collapsed, used for sidebars, IDE-style layouts, and mail-client panes.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Snap Panels to your project and fits it to your theme.

Code

How Snap Panels is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

"use client";

import { useRef, useState } from "react";
import { PanelLeft } from "lucide-react";

import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import {
  SnapPanel,
  SnapPanelGroup,
  SnapPanelHandle,
  type SnapPanelGroupHandle,
} from "@/components/ui/snap-panels";

const THREADS = [
  { from: "Priya Raman", subject: "Retry budgets", time: "09:41" },
  { from: "Marco Reyes", subject: "Token accounting", time: "09:06" },
  { from: "Dana Whitfield", subject: "Regional routing", time: "08:58" },
  { from: "Sofia Lindqvist", subject: "Stream backpressure", time: "08:31" },
  { from: "Omar Haddad", subject: "Structured outputs", time: "Yesterday" },
];

const initials = (name: string) =>
  name
    .split(" ")
    .map((part) => part[0])
    .join("");

export default function SnapPanelsCollapsibleExample() {
  const groupRef = useRef<SnapPanelGroupHandle>(null);
  const [collapsed, setCollapsed] = useState(false);

  return (
    <div className="h-64 w-full max-w-xl overflow-hidden rounded-lg border border-border bg-card">
      <SnapPanelGroup ref={groupRef} direction="horizontal">
        <SnapPanel
          id="threads"
          defaultSize="240px"
          minSize="180px"
          snapPoints={["240px", "320px"]}
          collapsible
          onCollapse={() => setCollapsed(true)}
          onExpand={() => setCollapsed(false)}
        >
          <div className="flex h-full flex-col gap-px bg-background p-1.5">
            {THREADS.map((thread) => (
              <div
                key={thread.from}
                className={cn(
                  "flex items-center gap-2 rounded-md px-2 py-1.5",
                  thread.from === "Priya Raman" && "bg-active",
                )}
              >
                <span className="flex size-6 shrink-0 items-center justify-center rounded-full bg-muted text-2xs text-muted-foreground">
                  {initials(thread.from)}
                </span>
                <div className="flex min-w-0 flex-col">
                  <span className="truncate text-caption text-foreground">
                    {thread.from}
                  </span>
                  <span className="truncate text-2xs text-muted-foreground">
                    {thread.subject}
                  </span>
                </div>
                <span className="ml-auto shrink-0 self-start font-mono text-2xs text-muted-foreground">
                  {thread.time}
                </span>
              </div>
            ))}
          </div>
        </SnapPanel>
        <SnapPanelHandle aria-label="Resize thread list" />
        <SnapPanel id="reading" minSize={30}>
          <div className="flex h-full flex-col">
            <div className="flex h-11 shrink-0 items-center gap-2 border-b border-border px-3">
              <Button
                variant="outline"
                size="sm"
                onClick={() => groupRef.current?.toggle("threads")}
                aria-pressed={collapsed}
              >
                <PanelLeft className="size-4" />
                {collapsed ? "Show threads" : "Hide threads"}
              </Button>
            </div>
            <div className="flex flex-col gap-2 p-3">
              <span className="text-control text-foreground">
                Retry budgets
              </span>
              <p className="text-caption text-muted-foreground">
                Collapse it with the button, then drag the divider back out.
                Both cross the dead band below the minimum width under your hand
                rather than jumping across it.
              </p>
            </div>
          </div>
        </SnapPanel>
      </SnapPanelGroup>
    </div>
  );
}

Dependencies

More by Purbayan Pramanik

View all
  • Notification Stack

    Notification Stack

    22
  • Proximity Accordion

    Proximity Accordion

    5
  • Plan Checklist

    Plan Checklist

  • Agent Activity

    Agent Activity

  • Checkbox Group

    Checkbox Group

  • Conversation

    Conversation