Back to Ghost

Patterns

apps/shade/src/docs/patterns-guide.mdx

6.50.02.0 KB
Original Source

import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="Patterns / Patterns Guide" /> <div className="sb-doc">

Patterns

<p className="excerpt">Product-shaped compositions. `PageHeader`, `KpiCard`, `Filters`, `GhAreaChart`. Patterns compose primitives, components, and recipes with knowledge of how Ghost actually uses them. Page-level wrappers like `ListPage` live in the separate [Page templates](?path=/docs/page-templates-page-types--docs) layer.</p>

Use a pattern when

  • The shape is one Ghost reuses across surfaces (page headers, list pages, KPI rows).
  • You'd otherwise be rebuilding the same chrome from primitives + components every time.
  • The page-type taxonomy on Page Types names it.

What patterns do (and don't)

DoDon't
Expose 3–6 named subcomponents (.Title, .Actions, .Body)Take a prop bag (title, onAdd, columns, emptyText …)
Compose components, primitives, recipesFetch data, own routing, read app context
Carry generic names (PageHeader, KpiCard)Carry surface-specific names (MembersFilterBar)
Stay stable across consumersAdd a prop every time a new consumer needs something

If you find useQuery inside a pattern, the boundary is wrong — bring-your-own state.

Promotion rules and the full decision flow: Layers. Agent rules: apps/shade/AGENTS.md.

Example

tsx
import {PageHeader} from '@tryghost/shade/patterns';
import {Button} from '@tryghost/shade/components';

<PageHeader>
  <PageHeader.Left>
    <PageHeader.Title>Members<PageHeader.Count>{count}</PageHeader.Count></PageHeader.Title>
  </PageHeader.Left>
  <PageHeader.Actions>
    <PageHeader.ActionGroup>
      <Button>Add member</Button>
    </PageHeader.ActionGroup>
  </PageHeader.Actions>
</PageHeader>

For the full list-page wrapper (ListPage composing PageHeader + body), see Page templates / Page types.

Browse the sidebar for the live list of patterns.

</div>