Components
Panel
The dashboard workhorse — a bordered, sharp-cornered card with an optional divided header (eyebrow, title, description, actions slot) and footer, wrapping any chart, table, or stat cluster.
Install
npx shadcn@latest add @alix/panelPreview
01 · Revenue
Net revenue vs. target
Trailing 12 months, $M
+5.4%
FY25 net revenue$248.6M
+12.4%vs. FY24
Usage
<Panel
eyebrow="01 · Revenue"
title="Net revenue vs. target"
description="Trailing 12 months, $M"
actions={<Button size="sm" variant="outline">Export</Button>}
>
<ChartContainer config={config}>{…}</ChartContainer>
</Panel>Examples
01
Body-only frame
Leave the header props empty when the surrounding view already names the content.
Engagement command centre
Three priority workstreams are ready for partner review.
<Panel>
<div className="space-y-2 text-sm">
<p className="font-medium text-foreground">Engagement command centre</p>
<p className="text-muted-foreground">
Three priority workstreams are ready for partner review.
</p>
</div>
</Panel>02
Flush table body
Use flush when a table owns its own cell padding and should meet the panel border.
Delivery risks
Open items by workstream
| Item | Owner | Health |
|---|---|---|
| Supplier baseline | A. Ward | At risk |
| Cash forecast | M. Khan | On track |
| Board pack | R. Silva | Off track |
<Panel title="Delivery risks" description="Open items by workstream" flush>
<DataTable
caption="Delivery risks"
dense
rows={[
{ item: "Supplier baseline", owner: "A. Ward", health: "At risk", tone: "warning" as const },
{ item: "Cash forecast", owner: "M. Khan", health: "On track", tone: "success" as const },
{ item: "Board pack", owner: "R. Silva", health: "Off track", tone: "destructive" as const },
]}
columns={[
{ key: "item", header: "Item", cell: (row) => row.item, cellClassName: "font-medium" },
{ key: "owner", header: "Owner", cell: (row) => row.owner },
{
key: "health",
header: "Health",
cell: (row) => <Badge variant={row.tone} dot size="sm">{row.health}</Badge>,
},
]}
/>
</Panel>03
Header action and footer
Actions stay in the header while source notes and secondary context sit below a footer rule.
Pipeline conversion
Qualified opportunities
Conversion rate42.8%
+3.1ptvs. last quarter
<Panel
title="Pipeline conversion"
description="Qualified opportunities"
titleTag="h2"
actions={<Button size="sm" variant="outline">Export</Button>}
footer="Updated from the CRM snapshot at 08:00."
>
<Stat
label="Conversion rate"
value="42.8%"
delta={<Delta value="+3.1pt" direction="up" intent="positive" />}
caption="vs. last quarter"
/>
</Panel>When to use it
Use it for
- Dashboard cells that frame charts, tables, or KPI clusters inside a shared grid.
- Header action slots for export, filter, status, or date controls that belong to one panel.
- Flush bodies when the child component already owns its internal padding.
Reach for something else
- Single headline KPIs with their own sparkline and footnote — use StatCard instead.
- Full application chrome with sidebar and topbar — use DashboardShell instead.
- Content sections on marketing or editorial pages — use SectionWrapper instead.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| title | ReactNode | — | Panel heading — the chart or table's name (UI typography, baked small). |
| eyebrow | ReactNode | — | Small mono eyebrow above the title — a section number, source, or group. |
| description | ReactNode | — | Quiet sub-line under the title — the metric definition or period. |
| actions | ReactNode | — | Header-right slot — a filter Select, a legend, a Button, a Badge. A composition slot, not config. |
| footer | ReactNode | — | Footer slot under a top divider — a note, source line, or link. |
| flush | boolean | false | Remove body padding so content meets the border. Use for a DataTable or full-bleed chart. |
| titleTag | "h2" | "h3" | "h4" | "div" | "h3" | Semantic tag for the title, set per the page's heading hierarchy. |