UI.MD

Page Title Row

The title bar for a page that is operated rather than read: one line, title at one end, controls at the other.

Installation

$
npx shadcn@latest add https://ui.mattdowney.com/r/page-title-row.json

Usage

import { PageTitleRow } from "@/components/ui/page-title-row"

Examples

Default

The bar every app route opens with. It holds a minimum height whether or not it carries controls, so the page body starts on the same line either way, and it ships no bottom margin: put it in a Stack.

Audience

NameEmailJoined
Maya Chenmaya@studio.coOct 2
Tom Reyestom@reyes.designSep 28
Ada Parkada@parkworks.ioSep 21

With a subtitle

A phrase inline beside the title, for a record's state or id. It shares the line and truncates, so keep it short; a sentence explaining the page belongs to the section it describes.

Edition 42

draft · saved 2m ago
StorySectionWords
Designing for the second visitFeature1,240
Five type pairings we keep usingNotes620
Reader mailLetters380

With a count

A count or a status pill beside the title goes in titleBadge. It centres on the title's cap band and contributes no height, so a page carrying a count and a page without it put their titles on the same line. Passing a Badge to subtitle instead renders it broken: that slot is text-only.

Playground

ItemTypeUpdated
Hero illustrationImageToday
Pricing tableComponentYesterday
Onboarding copyTextOct 4

With sub-nav

A center slot switches the row to three columns, for a section whose pages share one title. In a narrow row the title and button share the first line and the tabs take the next, since a tab set cannot share a phone with both.

Newsletter

Subscribers
12,480
vs. last month
Open rate
48%
vs. last month
Unsubscribes
36
vs. last month

Section

A titled group inside an app page, like a review queue's Pending and Done. The count goes in titleBadge, the action and a lone search in action; a set of filters goes in a TableToolbar under the row. Space the row from its one table at cluster and one section from the next at peer.

Pending

RequestCustomerAmount
#3390Sam Okafor$860
#3386Lena Brandt$215

Done

RequestCustomerAmount
#3389Julik Tarkhanov$410
#3385Andrew Culver$95

API Reference

PropTypeDefaultDescription
size"page" | "section""page"page: an h1 at text-lg in a 40px row, the route's own title bar. section: an h2 at text-base in a 32px row, for a titled group inside the page; its subtitle drops to text-1xs. A count in titleBadge never changes the row's height at either size.
title*ReactNode-The page title. A string takes the row's own type treatment; pass a node when the title IS a control (a breadcrumb, a record picker) and brings its own styling.
titleBadgeReactNode-A count or a status pill inline beside the title, e.g. a Badge reading "39 items" or "Live". Centred on the title's cap band rather than seated on its baseline, and it never sets the row's height. Not a control: anything pressable belongs in action.
subtitleReactNode-Secondary text inline beside the title. A phrase, not a sentence: it shares the line and truncates. Text only: a Badge or any other pill goes in titleBadge, since this slot's wrapper is an inline span carrying truncate and a pill renders broken inside it.
subtitleClassNamestring-Extra classes on the subtitle, e.g. hidden sm:inline to drop it on small screens rather than have it eat the title's width.
leadingReactNode-Slot before the title, e.g. a back button on an editor sub-page. Centred against the title line.
centerReactNode-Centre slot. Its presence switches the row to a three-column grid, for a section that carries sub-nav between the title and the actions.
actionReactNode-Right slot: buttons, a segmented control, a badge, a menu.
size"page" | "section" = "page"page: an h1 at text-lg in a 40px row, the route's own title bar. section: an h2 at text-base in a 32px row, for a titled group inside the page; its subtitle drops to text-1xs. A count in titleBadge never changes the row's height at either size.
title*ReactNodeThe page title. A string takes the row's own type treatment; pass a node when the title IS a control (a breadcrumb, a record picker) and brings its own styling.
titleBadgeReactNodeA count or a status pill inline beside the title, e.g. a Badge reading "39 items" or "Live". Centred on the title's cap band rather than seated on its baseline, and it never sets the row's height. Not a control: anything pressable belongs in action.
subtitleReactNodeSecondary text inline beside the title. A phrase, not a sentence: it shares the line and truncates. Text only: a Badge or any other pill goes in titleBadge, since this slot's wrapper is an inline span carrying truncate and a pill renders broken inside it.
subtitleClassNamestringExtra classes on the subtitle, e.g. hidden sm:inline to drop it on small screens rather than have it eat the title's width.
leadingReactNodeSlot before the title, e.g. a back button on an editor sub-page. Centred against the title line.
centerReactNodeCentre slot. Its presence switches the row to a three-column grid, for a section that carries sub-nav between the title and the actions.
actionReactNodeRight slot: buttons, a segmented control, a badge, a menu.