Patterns · Layout
SplitLayout
List and detail panes side by side, one pane at a time on narrow screens.
Inbox
No conversation selected
Pick one from the list.
import { useState } from 'react';
import { Button, EmptyState, SplitLayout } from '@rookcom/monarch-react';
function Inbox() {
const [open, setOpen] = useState<string | null>(null);
return (
<div style={{ height: '16rem', width: '100%' }}>
<SplitLayout
listLabel="Conversations"
detailLabel="Conversation"
detailOpen={open !== null}
onBack={() => setOpen(null)}
list={
<div style={{ display: 'grid', gap: '0.5rem', padding: '0.5rem' }}>
{['Billing question', 'Password reset'].map((t) => (
<Button key={t} variant="ghost" onClick={() => setOpen(t)}>
{t}
</Button>
))}
</div>
}
detail={
open ? (
<p style={{ padding: '1rem' }}>{open}</p>
) : (
<EmptyState title="No conversation selected" description="Pick one from the list." />
)
}
/>
</div>
);
}
<Inbox />Usage examples
The example at the top of the page is the only one for this component.
Purpose
Two-pane list + detail layout for inbox-style views. On wide screens both panes show; below 768px only one shows, chosen by detailOpen, with an optional back button.
Use when
- Browsing a list while reading one item (an inbox, a chat list, a document list).
Avoid when
- Detail that needs the full page: navigate to a detail page instead.
Design principles
- No routing inside: the app decides what is open and passes
detailOpen. - Each pane is a labelled region so screen-reader users can jump between them.
- Each pane scrolls on its own.
API reference
import { SplitLayout } from '@rookcom/monarch-react';| Export | Kind | Rendering | Root class |
|---|---|---|---|
SplitLayout | component | client | .mn-split |
Renders a div with two <section> panes; native div attributes pass through. No ref is forwarded.
Generated from the TypeScript declarations of packages/react/src/patterns/layout/split-layout.
Properties
SplitLayout
List + detail two-pane layout (Tickets inbox, Chat, Dossier). No routing inside.
| Prop | Type | Description |
|---|---|---|
detail required | ReactNode | The detail pane. When empty, pass an EmptyState. |
list required | ReactNode | The list pane (e.g. ticket list). |
backLabel | string | |
children | ReactNode | |
className | string | |
detailLabel | string | |
detailOpen | boolean | Below the `md` breakpoint only one pane shows. `detailOpen` picks which; `onBack` renders a back button in the detail pane. |
id | string | |
listLabel | string | |
listWidth | "sm" | "md" | "lg" | Width of the list pane on wide screens. |
onBack | (() => void) |
Variants
| Prop | Values | Description |
|---|---|---|
listWidth | 'sm''md''lg' | List pane width on wide screens: 18rem, 22rem (default) or 28rem. Exposed as data-list-width. |
States
| State | Exposed as | Behaviour |
|---|---|---|
| Detail open | data-detail-open | Below 768px, shows the detail pane instead of the list. |
Interaction behaviour
- With
onBack, the detail pane renders an IconButton (labelbackLabel, default "Back to list") that calls it. - The back button is hidden from 768px up.
Keyboard support
| Keys | Action |
|---|---|
| Tab | Moves through list content, then detail content (including the back button). |
Accessibility
- Role
- region (two labelled sections)
- Accessible name
listLabel(default "List") anddetailLabel(default "Detail").
- Give both panes specific labels, for example "Tickets" and "Ticket".
- Focus is not moved when
detailOpenchanges; the app should move it to the opened detail.
Responsive behaviour
- Below 768px: one column; the list shows unless
detailOpen, then the detail shows. - From 768px: list column at
listWidth, detail fills the rest. - The layout fills its container height (
height: 100%); give the container a height.
Theme support
- Pane divider and back-bar borders use Monarch border tokens.
Do and don't
Do
- Show an EmptyState in the detail pane when nothing is selected.
- Pass
onBackso narrow screens can return to the list.
Don't
- Do not nest SplitLayouts.
- Do not leave the container without a height.
Related components
- SplitInboxLayout: Planned three-pane inbox template (wave 8).
- EmptyState: Placeholder for an empty detail pane.
- PageHeader: Sits above the layout.
Dependencies
| Kind | Depends on |
|---|---|
| npm packages | None |
| Monarch components | Button |
Testing coverage
- Both panes are labelled regions.
- The back button calls onBack.
| Check | Covered |
|---|---|
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | None yet |
Tests: packages/react/src/patterns/layout/split-layout