← All components
overlays / stable

RecordDrawer

Non-modal record inspector. Control isOpen/onOpenChange; provide recordType, title and body children. propertyBar, comments, rail and footer are optional content slots.

LIVE PREVIEW
Loading preview…

When to use

  • Description and content sections precede comments; the comment stream precedes its composer. Supply recordId to copy a link beside the current breadcrumb with toast feedback.
  • Property chips and progress wrap inline at the main column’s top. The canvas rail starts on that same line and scrolls independently.
  • Inspect and edit a work item while keeping the list or board usable.
  • Use mode=create for a new record and supply its title editor and footer action; register pending edits so navigation waits for saves.

Consider an alternative

  • Full-width property bands or pushing progress to the far edge — keep summaries inline after chips.
  • Rail values without PropertyField rows, uppercase labels or bordered chips.
  • Never repeat the ID (breadcrumb owns it) or any property shown in the properties bar.
  • Unrelated modal forms: use Sheet.
  • Long documents without record properties: use a document page.

Import

import { RecordDrawer } from '@revido-llc/kit';

Keyboard interaction

  • J / K or ↓ / ↑ — next / previous record, after saving edits.
  • Enter or ⌘Enter — expand; Escape — collapse, then close.
  • Tab — properties and actions; editing fields keep their own keys.

Props

Props documented by the Kit. Refer to the TypeScript types for the complete interface, including inherited props.

children (required)ReactNode—
isOpen (required)boolean—
onOpenChange (required)(open: boolean) => void—
recordType (required)string—
title (required)ReactNode—
actionsRecordAction[]—
aria-labelstring—
bannerReactNode—
breadcrumbReactNode—Parent trail ending in the record ID. The breadcrumb owns identity.
commentsReactNode—Chronological activity after the content sections; the composer follows the newest entry.
embeddedbooleanfalseRender inside an existing overlay; the host owns focus, scrolling and keyboard events.
expandedboolean—
footerReactNode—
mode"view" | "create""view"
navigationRecordNavigation—
onBack(() => void)—
onExitComplete(() => void)—Forwarded to Peek — keep the drawer mounted until this fires so close can slide out.
onExpandedChange((expanded: boolean) => void)—
propertyBarReactNode—Compact, wrapping chips at the top of the main column; progress stays inline.
railReactNode—Independently scrolling canvas rail aligned with the main column below the header.
recordId{ label: string; copyValue: string; }—Current breadcrumb identity and link. Relative links resolve in the browser; feedback names the display ID.
size"record" | "md" | "sm""record"

Related exports

RecordPropertyBarEnd, RecordSection, RecordDecisionBar, RecordFooter, RecordCommentStream, CommentVisibilityBadge, usePendingEdits, useRecordPendingEdits

Examples

A complete feature

Property bar, description and content sections, then one chronological comment and event stream with its composer last. A quiet property rail scrolls alongside the main column.

<RecordDrawer isOpen={open} onOpenChange={setOpen} recordType="feature" title={<EditableTitle value={title} onChange={setTitle} />} recordId={{ label: "ACME-13", copyValue: "/projects/acme?feature=ACME-13" }} propertyBar={properties} comments={comments} rail={rail}>
  <RecordSection title="Tasks" count={3}>{tasks}</RecordSection>
</RecordDrawer>

Create a feature

The title receives focus. A clear footer explains when creation becomes available.

<RecordDrawer isOpen={open} onOpenChange={setOpen} mode="create" recordType="feature" title={<EditableTitle mode="input" value={title} onChange={setTitle} />} footer={<RecordFooter primary={{ label: "Create feature", onPress: create, isDisabled: !title.trim(), disabledReason: "Give the feature a title." }} cancel={{ onPress: close }} />}>{description}</RecordDrawer>

Compact peek

Rail groups stack beneath the main column; the title keeps two lines instead of truncating early.

<RecordDrawer size="sm" isOpen={open} onOpenChange={setOpen} recordType="feature" title={title} rail={rail}>{description}</RecordDrawer>

Shared with a client

Shared discussion and project context, with internal comments and team actions omitted.

<RecordDrawer isOpen={open} onOpenChange={setOpen} recordType="feature" title={<EditableTitle disabled value={title} onChange={setTitle} />} comments={sharedComments} propertyBar={readOnlyProperties} rail={rail}>{description}</RecordDrawer>

Decision with checklist

Callout feedback and explicit readiness share the drawer banner gutter.

<RecordDrawer banner={<RecordDecisionBar tone="warning" message="Finish the handoff before QA review." checklist={requirements} primary={submitForQa} />} … />

Comment visibility

One warning-tinted lock badge. Authors pass onPress to toggle a root comment; inherited replies remain static with the same size and tone.

<CommentVisibilityBadge isInternal />
<CommentVisibilityBadge isInternal inherited />

Literal anatomy

Static component anatomy; bind events in the consuming app.

<RecordDrawer isOpen={false} recordType="draft" title="Draft proposal"><RecordSection title="Details">Ready for review.</RecordSection><RecordPropertyBarEnd>3 points</RecordPropertyBarEnd><RecordDecisionBar tone="info" message="Ready for review" /><RecordCommentStream items={[]} composer="Write a comment" /><RecordFooter primary={{ label: "Save" }} /></RecordDrawer>

Literal title

A title value; bind changes in the consuming app.

<EditableTitle value="Draft proposal" />

Related components