List
Templatebeta@macaw-ds/blocks · since 0.1.0Structure for a list page: header with one primary action, a search and filter toolbar, and rows on a card.
Add to your project
Blocks are copied into your repo as a starting point. They import components from @macaw-ds/ui, so the components stay on the upgrade path while the composition is yours.
With the MACAW CLI
npx @macaw-ds/cli add list-templateWith shadcn
npx shadcn@latest add https://macaw-design-system.vercel.app/r/list-template.jsonSource
packages/blocks/src/list-template/list-template.tsx, exactly what the CLI copies.
import { Icon } from '@macaw-ds/icons';
import { Badge, Button, Card, Input, PageHeader, Text } from '@macaw-ds/ui';
import type * as React from 'react';
export interface ListTemplateProps {
/** The page title, rendered as the page's h1. */
title?: React.ReactNode;
/** One line under the title. */
description?: React.ReactNode;
/** Page actions: the page's one primary Button and its secondaries. */
actions?: React.ReactNode;
/** The row above the list: search, filters, bulk actions. */
toolbar?: React.ReactNode;
/** The list itself, rendered on a layer-1 Card with no padding. Usually a `ul` of rows. */
children?: React.ReactNode;
}
const PLACEHOLDER_ROWS = [
{ id: 'item-1', name: 'Item one', detail: 'Secondary detail', status: 'Active', value: '$0.00' },
{ id: 'item-2', name: 'Item two', detail: 'Secondary detail', status: 'Active', value: '$0.00' },
{ id: 'item-3', name: 'Item three', detail: 'Secondary detail', status: 'Draft', value: '$0.00' },
{ id: 'item-4', name: 'Item four', detail: 'Secondary detail', status: 'Draft', value: '$0.00' },
];
function PlaceholderToolbar() {
return (
<>
<div className="w-full max-w-80">
<Input
type="search"
aria-label="Search items"
placeholder="Search items"
startAdornment={<Icon name="search" />}
/>
</div>
<Button variant="secondary" iconStart={<Icon name="filter" />}>
Filter
</Button>
</>
);
}
function PlaceholderList() {
return (
<ul aria-label="Items" className="m-0 list-none p-0">
{PLACEHOLDER_ROWS.map((row) => (
<li
key={row.id}
className="layout-grid items-center border-b border-line-subtle px-card-x py-stack-sm last:border-b-0"
>
<div className="col-span-6 flex min-w-0 flex-col">
<Text variant="label-md" truncate>
{row.name}
</Text>
<Text variant="body-sm" tone="muted" truncate>
{row.detail}
</Text>
</div>
<div className="col-span-3">
<Badge tone={row.status === 'Active' ? 'success' : 'neutral'} size="sm">
{row.status}
</Badge>
</div>
<div className="col-span-3">
<Text variant="numeric-md" numeric align="end">
{row.value}
</Text>
</div>
</li>
))}
</ul>
);
}
/**
* List page: a PageHeader with the one primary action, a toolbar row, then the list on a
* layer-1 card. Renders the main area only; AppShell, TopBar and main come from the root layout.
*/
export function ListTemplate({
title = 'Items',
description = 'Every item in this collection, newest first.',
actions = <Button iconStart={<Icon name="plus" />}>New item</Button>,
toolbar = <PlaceholderToolbar />,
children = <PlaceholderList />,
}: ListTemplateProps) {
return (
<>
<PageHeader title={title} description={description} actions={actions} />
<div className="flex max-w-page flex-col gap-stack-md px-margin py-section">
<div className="flex flex-wrap items-center gap-2">{toolbar}</div>
<Card padding="none">{children}</Card>
</div>
</>
);
}Uses
The MACAW components this block composes.