Users
Page content goes here.
'use client'
import { Page } from '#components/ui/page'
import { Text } from '@chakra-ui/react'
export const PageBasic = () => {
return (
<Page.Root height="320px" borderWidth="1px" rounded="l3">
<Page.Header title="Users" />
<Page.Body>
<Text textStyle="sm">Page content goes here.</Text>
</Page.Body>
</Page.Root>
)
}
Anatomy
import { Page } from '#components/ui/page'<Page.Root>
<Page.Header title="Users" />
<Page.Body />
</Page.Root>Examples
With actions
Add toolbar actions to the page header. Use the xs size for buttons in the
header toolbar.
Users
Page content goes here.
'use client'
import { ButtonGroup, Text } from '@chakra-ui/react'
import { Button } from '#components/ui/button'
import { Page } from '#components/ui/page'
export const PageWithActions = () => {
return (
<Page.Root height="320px" borderWidth="1px" rounded="l3">
<Page.Header
title="Users"
actions={
<ButtonGroup justifyContent="flex-end">
<Button variant="outline" size="xs">
Export
</Button>
<Button variant="glass" colorPalette="accent" size="xs">
Invite
</Button>
</ButtonGroup>
}
/>
<Page.Body>
<Text textStyle="sm">Page content goes here.</Text>
</Page.Body>
</Page.Root>
)
}
With filters
Use the footer prop on Page.Header to render a filter bar
below the page title.
Accounts
Northstar Labs
Enterprise
ActiveKite & Harbor
Growth
TrialArcwell Health
Enterprise
ActiveFieldnote Studio
Starter
PausedCopperline
Growth
ActiveDaybreak Energy
Enterprise
Active'use client'
import {
Badge,
Box,
ButtonGroup,
HStack,
Stack,
Text,
} from '@chakra-ui/react'
import { Button } from '#components/ui/button'
import { createFilters } from '#components/ui/filters'
import { Page } from '#components/ui/page'
import { LuLayers } from 'react-icons/lu'
import { z } from 'zod'
interface Account {
id: string
company: string
plan: 'Enterprise' | 'Growth' | 'Starter'
status: 'Active' | 'Trial' | 'Paused'
}
const accounts: Account[] = [
{ id: '1', company: 'Northstar Labs', plan: 'Enterprise', status: 'Active' },
{ id: '2', company: 'Kite & Harbor', plan: 'Growth', status: 'Trial' },
{ id: '3', company: 'Arcwell Health', plan: 'Enterprise', status: 'Active' },
{ id: '4', company: 'Fieldnote Studio', plan: 'Starter', status: 'Paused' },
{ id: '5', company: 'Copperline', plan: 'Growth', status: 'Active' },
{ id: '6', company: 'Daybreak Energy', plan: 'Enterprise', status: 'Active' },
]
const statusDot = (color: string) => (
<Box boxSize="2" rounded="full" bg={color} />
)
const accountFilters = createFilters({
fields: {
status: {
type: 'enum',
label: 'Status',
schema: z.enum(['Active', 'Trial', 'Paused']),
operators: ['equals', 'not', 'in'],
defaultOperator: 'equals',
meta: { icon: statusDot('border.emphasized'), pluralLabel: 'statuses' },
options: [
{
value: 'Active',
label: 'Active',
meta: { icon: statusDot('green.solid') },
},
{
value: 'Trial',
label: 'Trial',
meta: { icon: statusDot('blue.solid') },
},
{
value: 'Paused',
label: 'Paused',
meta: { icon: statusDot('gray.solid') },
},
],
},
plan: {
type: 'enum',
label: 'Plan',
schema: z.enum(['Enterprise', 'Growth', 'Starter']),
operators: ['equals', 'not', 'in'],
defaultOperator: 'equals',
meta: { icon: <LuLayers />, pluralLabel: 'plans' },
options: [
{ value: 'Enterprise', label: 'Enterprise' },
{ value: 'Growth', label: 'Growth' },
{ value: 'Starter', label: 'Starter' },
],
},
},
})
function statusColor(status: Account['status']) {
if (status === 'Active') return 'green'
if (status === 'Trial') return 'blue'
return 'gray'
}
export const PageWithFilters = () => {
const conditions = accountFilters.useConditions()
const matches = conditions.useFilter(accounts)
return (
<conditions.Root>
<Page.Root height="380px" borderWidth="1px" rounded="l3">
<Page.Header
title="Accounts"
actions={
<ButtonGroup justifyContent="flex-end">
<Button variant="glass" colorPalette="accent" size="xs">
Add account
</Button>
</ButtonGroup>
}
footer={
<conditions.FilterBar px="var(--page-header-padding-x)" pb="3" />
}
/>
<Page.Body p="0">
<Stack gap="0" divideY="1px" divideColor="border">
{matches.map((account) => (
<HStack key={account.id} px="4" py="2.5" gap="4">
<Text textStyle="sm" fontWeight="medium" flex="1" truncate>
{account.company}
</Text>
<Text textStyle="sm" color="fg.muted">
{account.plan}
</Text>
<Badge size="sm" colorPalette={statusColor(account.status)}>
{account.status}
</Badge>
</HStack>
))}
{!matches.length ? (
<Box px="4" py="8">
<Text textStyle="sm" color="fg.muted" textAlign="center">
No accounts match these filters.
</Text>
</Box>
) : null}
</Stack>
</Page.Body>
</Page.Root>
</conditions.Root>
)
}
With data table
Combine the page layout with a data table to build list views.
Accounts
Arcwell Health | Enterprise | Active | $9,800 |
Copperline | Growth | Active | $4,700 |
Daybreak Energy | Enterprise | Active | $15,100 |
Fieldnote Studio | Starter | Paused | $890 |
Kite & Harbor | Growth | Trial | $3,200 |
Northstar Labs | Enterprise | Active | $12,400 |
'use client'
import { ButtonGroup } from '@chakra-ui/react'
import { Button } from '#components/ui/button'
import {
createDataTableColumnHelper,
useDataTable,
} from '#components/ui/data-table'
import { Page } from '#components/ui/page'
interface Account {
id: string
company: string
plan: 'Enterprise' | 'Growth' | 'Starter'
status: 'Active' | 'Trial' | 'Paused'
mrr: number
}
const accounts: Account[] = [
{
id: '1',
company: 'Northstar Labs',
plan: 'Enterprise',
status: 'Active',
mrr: 12400,
},
{
id: '2',
company: 'Kite & Harbor',
plan: 'Growth',
status: 'Trial',
mrr: 3200,
},
{
id: '3',
company: 'Arcwell Health',
plan: 'Enterprise',
status: 'Active',
mrr: 9800,
},
{
id: '4',
company: 'Fieldnote Studio',
plan: 'Starter',
status: 'Paused',
mrr: 890,
},
{
id: '5',
company: 'Copperline',
plan: 'Growth',
status: 'Active',
mrr: 4700,
},
{
id: '6',
company: 'Daybreak Energy',
plan: 'Enterprise',
status: 'Active',
mrr: 15100,
},
]
const statusPalette = (value: string) =>
value === 'Active' ? 'green' : value === 'Trial' ? 'blue' : 'gray'
const columnHelper = createDataTableColumnHelper<Account>()
const columns = columnHelper.columns([
columnHelper.accessor('company', {
header: 'Account',
cell: ({ cell }) => <cell.TextCell />,
size: 220,
sortFn: 'text',
}),
columnHelper.accessor('plan', {
header: 'Plan',
cell: ({ cell }) => <cell.TextCell />,
size: 140,
}),
columnHelper.accessor('status', {
header: 'Status',
cell: ({ cell }) => <cell.BadgeCell colorPalette={statusPalette} />,
size: 140,
}),
columnHelper.accessor('mrr', {
header: 'MRR',
cell: ({ cell }) => (
<cell.NumberCell
currency="USD"
maximumFractionDigits={0}
style="currency"
/>
),
meta: { isNumeric: true },
size: 140,
}),
])
export const PageWithDataTable = () => {
const table = useDataTable({
columns,
data: accounts,
getRowId: (row) => row.id,
initialState: {
sorting: [{ id: 'company', desc: false }],
},
})
return (
<Page.Root height="380px" borderWidth="1px" rounded="l3">
<Page.Header
title="Accounts"
actions={
<ButtonGroup justifyContent="flex-end">
<Button variant="glass" colorPalette="accent" size="xs">
Add account
</Button>
</ButtonGroup>
}
/>
<Page.Body p="0">
<table.Provider>
<table.Root>
<table.ScrollArea>
<table.Table aria-label="Accounts" />
</table.ScrollArea>
</table.Root>
</table.Provider>
</Page.Body>
</Page.Root>
)
}
Settings variant
Use the settings variant for settings-style pages with larger headings. Pair
the title with the description prop for a subtitle.
Settings
Settings content goes here.
'use client'
import { Text } from '@chakra-ui/react'
import { Page } from '#components/ui/page'
export const PageSettings = () => {
return (
<Page.Root variant="settings" height="320px" borderWidth="1px" rounded="l3">
<Page.Header
title="Settings"
description="Manage your workspace preferences"
/>
<Page.Body>
<Text textStyle="sm">Settings content goes here.</Text>
</Page.Body>
</Page.Root>
)
}
Loading
Set loading on Page.Root to show a loading overlay in the body.
Users
'use client'
import { Text } from '@chakra-ui/react'
import { Page } from '#components/ui/page'
export const PageLoading = () => {
return (
<Page.Root loading height="320px" borderWidth="1px" rounded="l3">
<Page.Header title="Users" />
<Page.Body>
<Text textStyle="sm">This content is hidden while loading.</Text>
</Page.Body>
</Page.Root>
)
}
Props
Root
| Prop | Default | Type |
|---|---|---|
variant | 'panel' | 'panel' | 'settings'The visual style of the page |
loading | booleanShow a loading overlay in the page body | |
skeleton | React.ReactNodeCustom loading state, rendered in the body while loading |
Header
| Prop | Default | Type |
|---|---|---|
nav | React.ReactNodePage header navigation. Typically breadcrumbs or a back button | |
title | React.ReactNodePage header title | |
description | React.ReactNodePage header description | |
actions | React.ReactNodePage header actions, typically a toolbar | |
footer | React.ReactNodePage header footer, typically tabs |