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/panel

Preview

01 · Revenue

Net revenue vs. target

Trailing 12 months, $M

+5.4%
FY25 net revenue$248.6M
+12.4%vs. FY24
Source: Group FP&A

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

Delivery risks
ItemOwnerHealth
Supplier baselineA. WardAt risk
Cash forecastM. KhanOn track
Board packR. SilvaOff 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
Updated from the CRM snapshot at 08:00.
<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

PropTypeDefaultDescription
titleReactNodePanel heading — the chart or table's name (UI typography, baked small).
eyebrowReactNodeSmall mono eyebrow above the title — a section number, source, or group.
descriptionReactNodeQuiet sub-line under the title — the metric definition or period.
actionsReactNodeHeader-right slot — a filter Select, a legend, a Button, a Badge. A composition slot, not config.
footerReactNodeFooter slot under a top divider — a note, source line, or link.
flushbooleanfalseRemove 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.

See also