Search

npm run build writes a search index under site/api/ and bundles a React runtime that mounts into the <Search /> placeholder or [data-canopy-search]. Works (IIIF manifests) and pages (MDX files) share a single FlexSearch store so filters and type tabs stay in sync.

Data sources

  • Works — every manifest pulled from collection URIs becomes a type: 'work' record with id, title, and href pointing at /works/<slug>.html. Metadata fields listed in search.index.metadata are flattened for indexing.
  • Pages — MDX files contribute type: 'page' records when they have no front matter or when they explicitly set type. Add search: false to opt out. Optional searchSummary and searchSummaryMarkdown fields override the teaser.

packages/app/lib/build/search-index.js combines the records and keeps IDs stable between search-index.json (FlexSearch input) and search-records.json (display data).

Page composition

Create content/search/_layout.mdx to control the markup surrounding the runtime. The layout receives a search prop with form, summary, count, and results placeholders. Drop them anywhere in the document to change the layout without editing React code.

content/search/_layout.mdx
---title: Search--- <section>  <h1>{props.title}</h1>  {props.search.summary}  {props.search.form}  {props.search.count}</section> {props.search.results}

If you omit _layout.mdx, Canopy renders a fallback layout that stacks the form, summary, and results vertically.

Tabs, layouts, and templates

Control the rendered tabs and markup via search.results in canopy.yml:

canopy.yml
search:  results:    work:      layout: grid      # grid | list      result: figure    # figure | article    page:      layout: list      result: article  tabs:    order:      - work      - page
  • layout switches between the masonry grid (uses react-masonry-css) and a stacked list.
  • result picks the MDX partial that renders each item. Define _result-figure.mdx or _result-article.mdx under content/search/ to override the defaults. Each file receives the record (props.record), the active query, and helper props for badges or metadata.
  • tabs.order reorders tabs without redefining layout/result. Types omitted from results inherit the default grid + figure combo.

Filtering and URL state

The runtime watches ?q and ?type query params, so deep links remain stable when you switch between tabs or refine a search. When you add more record types via front matter (searchType: example), include them in search.results and tabs.order to expose dedicated tabs.

Styling hooks

  • Use the .canopy-search class (root element) to add custom spacing or background treatments in app/styles/main.css.
  • Override button or input styles globally via the Tailwind preset or your own CSS variables in @theme.
  • When you need different markup for specific record types, create additional templates under content/search/ and branch based on record.type inside the MDX.

Debugging tips

  • Inspect site/api/search-index.json to confirm front-matter summaries and metadata labels are being indexed.
  • When the browser console logs Dynamic require of 'react' is not supported, make sure the UI build marks react, react-dom, react-dom/client, react-masonry-css, and flexsearch as externals (the repository is already configured this way).
  • Delete .cache/search (if present) plus site/api/search-* and rebuild when you change record shapes.

Metadata browse

Canopy builds /metadata automatically. It groups the metadata labels you list in canopy.yml (metadata array) and renders links to every unique value found across the ingested manifests. Each link jumps directly to search results filtered to that label/value pair.

canopy.yml
metadata:  - Subject  - Creator  - Date  - Location

During the IIIF build, Canopy generates /api/facet/<label>/<value>.json collections for those labels. The metadata page uses the files to calculate counts and to build the links used elsewhere (home sliders, related items, search filters).

Page anatomy

  1. Intro copycontent/metadata/index.mdx controls the opening paragraph and any contextual guidance you want to provide to visitors.
  2. Facet sections — For each configured label Canopy shows the heading and a list of values, ordered by descending occurrence. Each value links to /search?type=work&label=<label>&value=<value>.
  3. Counts — Values display the number of works that match the label/value pair so users can gauge scale before clicking through.

Editing tips

  • Keep facet labels short and consistent; the UI uses the label verbatim as the section heading.
  • Remove labels from canopy.yml when you stop using them to avoid generating unused /api/facet/** files.
  • Pair metadata browse sections with MDX essays that explain curatorial choices. Cite those works via referencedManifests so the relationship flows back to individual work pages.