Skip to content

Components · Forms

TemplatePicker

Choose a template from a gallery: category filters, search, thumbnails, a blank option, a preview pane and arrow-key grid navigation.

@rookcom/monarch-react/patternsComponent

Gallery with categories, search, blank and preview

ClassicNavigation on the side, content on the right.

Usage examples

Compact, without search

Purpose

One gallery for every “start from a template” choice: page layouts, issue and ticket templates, form starters. The product supplies the templates and their thumbnails and decides what choosing one does. Import from @rookcom/monarch-react/patterns.

Use when

  • People pick one starting point from several visual options.
  • The options benefit from thumbnails, categories or search.

Avoid when

  • Two to five short text options: use SegmentedControl or a radio group.
  • A long list of named items without visuals: use Select or Combobox.

Design principles

  • A real radio group: one choice, one Tab stop, arrows to move, so selection is announced.
  • The thumbnail shows, the text tells: previews are decorative and every card has a name.
  • Starting from scratch is always an option when blank is set, and it comes first.
  • Filters narrow, they never hide the choice silently: the result count is announced and an empty result says so.
  • The selected card is marked by an accent ring and a check, not colour alone.

API reference

import { TemplatePicker, templateMatches } from '@rookcom/monarch-react/patterns';
ExportKindRenderingRoot class
TemplatePickercomponentclient.mn-template-picker
templateMatchesutilityclient–

Renders a <div> (ref) with the filters, a role="radiogroup" of role="radio" card buttons, and an optional preview <section aria-label="Preview">.

Templates are { id, name, description?, category?, preview?, badge?, keywords?, disabled? }; onValueChange(id, template) receives null as the template for the blank card.

templateMatches(template, query) is the search rule (every word must appear in the name, a string description or the keywords).

Cards expose data-state="checked|unchecked" and, for the blank card, data-blank.

Generated from the TypeScript declarations of packages/react/src/components/forms/template-picker.

Properties

TemplatePicker

Choose a template from a gallery: category filters, search, a blank option and an optional preview pane. The cards are a radio group with one Tab stop; arrow keys move through the grid by row and column and select as they go. The product supplies the templates and applies the choice.

PropTypeDescription
templates requiredreadonly TemplatePickerTemplate[]The templates, from the product.
blankboolean | TemplatePickerBlankAdds a "start from scratch" card first. `true` uses the defaults.
categoriesreadonly TemplatePickerCategory[]Category filters, in order. Shown when given (with an "All" filter first).
childrenReactNode
classNamestring
defaultCategorystringInitial category id. Default: all.
defaultValuestring | nullInitial selection (uncontrolled).
idstring
messagesPartial<TemplatePickerMessages>Override visible and announced strings.
minCardWidthstringNarrowest a card gets before the grid drops a column. Default `9rem`.
onValueChange((id: string, template: TemplatePickerTemplate | null) => void)Called with the chosen id and template (`null` for the blank card).
renderPreview((template: TemplatePickerTemplate | null) => ReactNode)Larger preview of the selected template, shown beside the grid (below it on narrow screens). Receives `null` for the blank card.
searchablebooleanShow the search box. Default true.
valuestring | nullSelected template id (controlled); `null` for none.

Variants

PropValuesDescription
blank'false''true''{ id, name, description, preview }'Adds a dashed “start from scratch” card first (id blank by default).
renderPreview'undefined''(template) => ReactNode'A larger preview of the selected card: beside the grid from 48rem of width, below it otherwise.
minCardWidth'9rem (default)''any CSS length'The narrowest a card gets; the grid fits as many columns as that allows.

States

StateExposed asBehaviour
Selectedaria-checked="true", data-state="checked"Accent ring and a check on the thumbnail.
Hover–Stronger border.
Focus–Focus ring outside the card.
DisableddisabledFaded and skipped by the arrow keys.
Category onaria-pressed="true"Accent-tinted filter chip.
No results–“No templates match …” under the grid; the count is announced.

Interaction behaviour

  • Clicking a card selects it and calls onValueChange. Arrow keys move and select, like any radio group.
  • Category chips and search filter the cards; the blank card always stays.
  • If the selected card is filtered out, the selection is kept and the Tab stop moves to the first visible card.
  • Applying the template (filling fields, creating a page) is the product’s job, usually behind a Continue button.

Keyboard support

KeysAction
TabMoves into the grid (to the selected card, or the first one) and out of it.
Arrow Left / RightPrevious or next card, wrapping; mirrored in right-to-left.
Arrow Up / DownThe card above or below in the grid, by the number of columns shown.
Home / EndFirst or last card.
Space / EnterSelects the focused card.

Disabled cards are skipped. The search box and chips come before the grid in Tab order.

Accessibility

Role
radiogroup of radio
Accessible name
aria-label (default “Templates”) or aria-labelledby.
  • Each card is named by the template name and described by its badge and description.
  • Thumbnails and the check are aria-hidden; nothing depends on seeing them.
  • The filter chips are toggle buttons (aria-pressed) in a group named “Categories”; the search box is a named searchbox.
  • The result count is a visually hidden polite status that updates as filters change.
  • Verified with axe in jsdom and in Chromium, Firefox and WebKit on /components/template-picker.

Responsive behaviour

  • The grid uses as many columns as fit (minCardWidth), down to one at 320px.
  • Filters wrap: the search box takes a full line on narrow screens, chips wrap below.
  • The preview pane sits beside the grid from 48rem of the picker’s own width (container query) and below it otherwise.

Theme support

  • Monarch tokens only: --mn-color-accent-solid for the selected ring and check, --mn-color-bg-subtle behind thumbnails.
  • Thumbnails are the product’s; draw them with tokens (as the examples do) so they follow light, dark and high contrast.
  • Forced-colours mode outlines the selected card in Highlight; transitions stop under prefers-reduced-motion.

Do and don't

Do

  • Give every template a short, distinct name; the name is what screen readers announce.
  • Draw thumbnails with tokens or the customer’s brand colour so they look like the real result.
  • Confirm with a separate Continue or Use template button when choosing has side effects.

Don't

  • Do not apply a template the moment a card is selected; arrow keys select as they move.
  • Do not put essential information only in the thumbnail.
  • Do not use it for more than a few dozen templates without categories and search.

Dependencies

KindDepends on
npm packagesreact
Monarch componentsSearchInput

Testing coverage

  • Named radiogroup of radio cards, blank first, names and descriptions, disabled card, one Tab stop; axe.
  • Click selects (uncontrolled) and reports the id and template; blank reports null.
  • Arrow keys move and select, skip disabled cards and wrap; Home and End.
  • Up and Down move by the number of columns (layout simulated in jsdom).
  • Category chips and search filter; result count announced; no-results message; axe.
  • Controlled value and the preview pane, including the blank card.
  • templateMatches rules; translated messages; renders on the server.
CheckCovered
Axe in jsdomYes
Server renderingYes
Browser suitesVisual baselines: /__gallery/template-picker; Cross-browser axe: /components/template-picker

Tests: packages/react/src/components/forms/template-picker