Sidebar Drop Zones

The two renderers behind a zone definition: a full-height strip pinned to an edge of the panel, and an inline strip that opens in the scroll flow.

Open in

Drag a chat onto one of the zones.

Installation

npx shadcn@latest add miskibin/chat-components/sidebar-drop-zones

Also installs sidebar-dnd.

Usage

"use client"

import {
  SidebarDropZone,
  useSidebarDnd,
} from "@/components/ui/chat-sidebar"

export function ArchiveZone() {
  const { activeId } = useSidebarDnd()

  return (
    <SidebarDropZone
      id="archive-zone"
      label="Drop here to archive"
      icon={<Archive className="size-3.5" />}
      tone="muted"
      visible={!!activeId}
    />
  )
}

Examples

Edge zones without lifting a finger

Declare zones with an edge and let ChatSidebar mount and unmount them around the drag.

<ChatSidebarDnd
  zones={[
    pinDropZone(),
    trashDropZone({ label: "Move to trash" }),
  ]}
  onDrop={handleDrop}
>
  <ChatSidebar collapsed={collapsed} onCollapsedChange={setCollapsed} edgeZones>
    …
  </ChatSidebar>
</ChatSidebarDnd>

Restyle a zone

tone covers the three standard treatments; surfaceClassName reaches the dashed surface itself for anything else.

<SidebarDropZone
  id="archive-zone"
  label="Archive"
  tone="muted"
  visible={!!activeId}
  surfaceClassName="rounded-full border-solid py-2 text-[11px] uppercase tracking-wide"
/>

API reference

SidebarDropZone (inline)

id*string
Must match the id of a zone declared on the provider, or the drop resolves as kind: "custom".
visible*boolean
Collapses to zero height when false. Drive it from useSidebarDnd().activeId.
label*React.ReactNode
Zone text.
iconReact.ReactNode
Leading icon.
tone"accent" | "danger" | "muted" = "accent"
Color treatment, including drag-over.
className / surfaceClassNamestring
Merged onto the wrapper, and onto the dashed surface.

SidebarEdgeDropZone

edge"top" | "bottom" = "top"
Which edge of the sidebar panel it anchors to.
id / label / icon / tonesame as above
Mount it only while a drag is active — it animates itself in. ChatSidebar edgeZones does this for you.

Helper

dropZoneProps(zone){ id, label, icon, tone }
Turns a zone definition into the props both components take.

Data slots

Every part below is addressable from a parent, so you can restyle internals without forking the component.

sidebar-drop-zonesidebar-edge-drop-zone