ComponentsPage layouts

Record detail layout

The "show one record" page: a record header (media, h1, status, key facts, actions, previous/next pager), a main column for tabs, activity and related lists, and a side column of details. It responds to its own width — below 42rem (672px) of layout width the side column moves under the header, its sections fold and the key facts become a scrolling strip. It measures itself, not the viewport.

CSS grid + container queries · Radix Collapsible (aside sections) components/record-detail-layout.tsx, styles/navigation.csstheme · material · accent · density · font
Contacts

Laura Callahan

Customer

Head of Operations · Northwind Traders

Owner
Meltem Aydın
Stage
Renewal · Proposal
Lifetime value
$48,500
Last contact
Today, 11:42

Activity

Calls, emails and notes from everyone on the account.

    1. Meltem Aydın logged a call · 14 min
      Agreed on the 3-year option if onboarding hours are included.
    1. Laura Callahan replied to “Renewal — revised quote”
    1. Can Demir moved Northwind renewal to Proposal
    1. Meltem Aydın added a note
      Board meets on December 3 — wants a signed draft before then.

Installation

$ pnpm add @vamos-ui/react

The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";

Usage

import { RecordLayout, RecordHeader, RecordPager, RecordMain, RecordSection, RecordAside, RecordAsideSection } from "@vamos-ui/react";

<RecordLayout>
  <RecordHeader
    back={{ href: "/crm/contacts", label: "Contacts" }}
    media={<Avatar name="Laura Callahan" size="xl" />}
    title="Laura Callahan"
    subtitle="Head of Operations · Northwind Traders"
    status={<Badge tone="success">Customer</Badge>}
    facts={[{ label: "Owner", value: "Meltem Aydın" }, { label: "Lifetime value", value: "$48,500" }]}
    actions={<Button variant="primary">Log call</Button>}
    pager={<RecordPager index={3} total={128} prevHref="/crm/contacts/41" nextHref="/crm/contacts/43" />}
  />
  <RecordMain>
    <RecordSection title="Activity"><ActivityTimeline items={activity} /></RecordSection>
  </RecordMain>
  <RecordAside>
    <RecordAsideSection title="Details"><DescriptionList items={details} /></RecordAsideSection>
  </RecordAside>
</RecordLayout>

Examples

Resizable side column

resizable adds a window-splitter handle on the side column's edge: drag it, or focus it and use ←/→ (Shift ×4), Home/End; double-click or Enter resets.

Side column: 340px

Laura Callahan

Customer

Head of Operations · Northwind Traders

Owner
Meltem Aydın
Stage
Renewal · Proposal
Lifetime value
$48,500
Last contact
Today, 11:42

Activity

Calls, emails and notes from everyone on the account.

    1. Meltem Aydın logged a call · 14 min
      Agreed on the 3-year option if onboarding hours are included.
    1. Laura Callahan replied to “Renewal — revised quote”
    1. Can Demir moved Northwind renewal to Proposal
    1. Meltem Aydın added a note
      Board meets on December 3 — wants a signed draft before then.

Previous / next

RecordPager shows "1 of 4" and registers j (next) and k (previous) — ignored while typing. With prevHref/nextHref the buttons are links and the shortcuts navigate through VamosProvider.

People

Selin Öztürk

Active

Product designer · Design

Employee since
Mar 2023
Manager
Burak Şahin
Location
Istanbul office

Narrow container

Below 672px of layout width the aside sits under the header, every section folds and the facts scroll sideways.

Contacts

Laura Callahan

Customer

Head of Operations · Northwind Traders

Owner
Meltem Aydın
Stage
Renewal · Proposal
Lifetime value
$48,500
Last contact
Today, 11:42

Activity

Calls, emails and notes from everyone on the account.

    1. Meltem Aydın logged a call · 14 min
      Agreed on the 3-year option if onboarding hours are included.
    1. Laura Callahan replied to “Renewal — revised quote”
    1. Can Demir moved Northwind renewal to Proposal
    1. Meltem Aydın added a note
      Board meets on December 3 — wants a signed draft before then.

States

state="loading" renders a skeleton page; not-found and forbidden render a full-page empty state with the page's h1. readOnly hides header actions.

API

RecordLayout

PropTypeDefault
asidePosition
Logical side of the side column (flips in RTL).
"end" | "start""end"
asideWidth · defaultAsideWidth · onAsideWidthChange
Side column width in px, controlled or not.
number320
resizable
Window-splitter handle on the side column.
booleanfalse
minAsideWidth · maxAsideWidth
Resize limits.
number260 · 520
state
Built-in page states.
"ready" | "loading" | "not-found" | "forbidden""ready"
notFound · forbidden
Replace the default state pages.
ReactNode—
readOnly
Hides RecordHeader actions.
booleanfalse
labels
Overrides for the record namespace.
RecordLayoutLabels—

RecordHeader

PropTypeDefault
title
The page's <h1>.
ReactNode—
subtitle · status · media
Role and company, a status badge, an avatar or logo.
ReactNode—
facts
Key facts as a <dl>; a scrolling strip at phone width.
{ label: ReactNode; value: ReactNode }[]—
actions
Header buttons (hidden when readOnly).
ReactNode—
back
"← Contacts" link.
{ href: string; label: string }—
pager
A RecordPager at the top end.
ReactNode—
saving
"Saving…" status next to the title.
boolean—
size
Title 20 / 28 px × --display-scale.
"sm" | "md""md"
loading
Skeleton title and subtitle.
boolean—

RecordPager

PropTypeDefault
index · total
1-based position and list length: "3 of 128".
number—
prevHref · nextHref
Links; j/k navigate through VamosProvider.
string—
onPrevious · onNext
Callbacks instead of links.
() => void—
shortcuts
Register j / k (aria-keyshortcuts).
booleantrue

RecordMain · RecordSection

PropTypeDefault
tabs
RecordMain: route tabs above the content.
ReactNode—
title · description · action
RecordSection: a titled card (<section aria-labelledby>).
ReactNode—
padding
RecordSection body padding.
"md" | "none""md"

RecordAside · RecordAsideSection

PropTypeDefault
aria-label
RecordAside: name of the complementary landmark.
string"Details"
title · action
RecordAsideSection header.
ReactNode—
collapsible
Fold on wide layouts too (narrow layouts always fold).
booleanfalse
defaultOpen · open · onOpenChange
Open state.
booleantrue
headingLevel
Heading level of the title.
2 | 32

SidePaneHandle

PropTypeDefault
value · onValueChange
Pane width in px. Shared by Record detail and Editor layouts for custom side columns.
number—
min · max · defaultValue
Limits and the reset width.
number—
edge
Which edge of the pane the handle sits on.
"start" | "end""start"
step
Arrow-key step (Shift ×4).
number16