Search & Explore
Once records are in, the ExploreBlock ships a complete browse surface: per-tenant Orama index, faceted sidebar, search bar, sort, pagination — driven by the DataSchema annotations.
Two surfaces, one engine
| Surface | File | Used for |
|---|---|---|
/[tenantDomain]/explore | src/app/(frontend)/[tenantDomain]/explore/page.tsx | Full-screen dashboard, sidebar + cards + map |
ExploreSectionBlock | src/domains/cms/components/blocks/explore-section-block.tsx | Page-builder section, contained cards listing |
ExploreBlock | src/domains/cms/components/blocks/explore-block/index.tsx | Shared component used by both |
Both consume the same per-tenant Orama index. The full-screen dashboard is auto-mounted; the section block can be added to any page via the page builder or shipmore page build.
Variants
ExploreBlock is a multi-purpose atom that renders the same data in structurally different ways. The variant decides the renderer tree and the chrome (facets, search, sort, pagination):
type ExploreVariant = 'grid' | 'table' | 'gallery' | 'feed' | 'comparison' | 'map' | 'minimal'| Variant | Renderer | Facets | Search | Sort | Pagination |
|---|---|---|---|---|---|
grid | GridRenderer | sidebar | yes | yes | footer |
table | TableRenderer | topbar | yes | no | footer |
feed | FeedRenderer | topbar | no | yes | infinite |
map | MapRenderer | sheet | yes | no | none |
minimal | MinimalRenderer | none | no | no | none |
Pick the variant that matches the project shape: directories → grid, data platforms → table, signal sites → feed, geographic data → map, embedded teasers → minimal.
Variant is structural renderer dispatch, not class-utility variance. Use Tailwind/shadcn for visual polish inside a renderer; use the variant lookup map for swapping the whole tree.
Detail pages
Every record has a public detail page at /[tenantDomain]/explore/[slug]. The current first-pass template handles every archetype by reading the schema:
- Hero —
imageorlogofield - Title + subtitle + verified + rating — header
- Categories —
category-roled values - Description —
summary+ long-form text fields - Facts table —
factPrimary,factSecondary, plus unrouted facetable numerics - Map embed — only if
locationfield exists - Sidebar — facetable numerics (“at a glance”)
- JSON-LD — auto-emitted per record
Same template, different content per archetype.
Orama specifics
- In-memory — the index lives in the Node process as a module-level singleton. Cold start rebuilds from
records. - Per-tenant — one index per tenant; never cross-contaminated.
- Synchronous — Orama v3+ calls are sync; plays well with React Server Components.
- Edge-incompatible — never set
runtime = 'edge'on Present routes; the singleton needs Node module state. - What’s indexed — only fields with
searchable: true(full-text) orfacetable: true(filter/aggregate).cardVisibility: 'detail-only'does not affect indexing.
For very high tenant × listing counts, swap to Orama Cloud (or the parked Typesense / Meilisearch adapter) by replacing src/domains/present/orama/. The component contract stays the same.
Tuning the index
After changing searchable, facetable, or field types in the DataSchema, run:
shipmore schema apply --tenant <domain> --fields-file …
shipmore import --tenant <domain> --reindex # rebuild without re-importing rows(Or restart the Node process; cold start rebuilds the index from records.)
SEO
- Sitemap entries auto-generated for every published record at
/[tenantDomain]/explore/[slug]. - JSON-LD per record on the detail page (driven by the role taxonomy —
title,image,rating,factPrimary, etc.). next-sitemapconfig atnext-sitemap.config.cjsincludes per-tenant sitemaps.- Pagefind is wired for the docs site; for tenant content, Orama is the search.
See Features → Content for the broader page builder context.