Skip to content
AstroCraft Docs
On this theme

Browse Hubs & Index Pages

Indexa has three browse hubs, and they are the same page three times. /used-cars/, /new-cars/ and /electric-cars/ each compose BrowseHeader, BrowseToolbar and BrowseResults from Sections/Browse/, wrapped in a data-browse element that the client engine looks for.

What differs is the data. /used-cars/ reads browseData and carRecords from carsData.json.ts; the other two read newCarsBrowse/newCarRecords and electricBrowse/electricCarRecords from hubsData.json.ts. The blocks are deliberately the same shape, so a fourth hub — vans, or a make — is a new data block and a thirty-line route file, not a new set of sections.

<div data-browse>
  <BrowseHeader header={browseData.header} />
  <BrowseToolbar toolbar={browseData.toolbar} total={carRecords.length} />
  <BrowseResults rail={browseData.rail} results={browseData.results} cars={carRecords} />
</div>

The record count in the toolbar is carRecords.length, not a written number. The editorial figures around it — the index-wide match count, the per-option counts in the rail — are placeholders awaiting a live feed, and the source says so where they sit.

The filter rail

FilterRail renders the rail from browseData.rail.groups. Each group is a FilterGroupProps: a heading, either a set of fields (a text input or a select) or a set of checkbox options, and an optional collapsed flag that renders the group as a <details> with an “N options” summary.

The used-cars rail has ten groups:

Group Shape
Location postcode input + distance select (5/10/25/50 miles)
Price minimum + maximum selects, plus two checkbox options
Make 6 options, with an “all 84 makes” link
Body type 8 options
Fuel 5 options
Transmission 2 options
Mileage from + to selects
Year 2 banded options, filtered on min/max
Seller 3 options
Features 5 options — the fsh/warranty/mot/finance/delivery slugs

The city page at /specialists/classic-restoration/london/ reuses the same rail component and the same engine with five groups of its own — borough, discipline, facilities, established and availability — over eight specialist records rather than cars. That is the whole reason the engine maps a group key to a data attribute rather than knowing about cars: one filter engine, two record types. Browse & Filter covers how it works.

Without JavaScript the rail still renders, every accordion still opens, and every record stays visible. The engine only ever hides things.

The reviews hub

/reviews/ is a shelf for the blog’s verdicts, and it is a good example of an editorial page that reads real content rather than a parallel copy of it. The featured band and the shelf cards come from getCollection("blog") filtered to posts carrying the "Review" category, newest first. The rest of the page — the masthead, the verdict criteria, the owner quotes, the make columns, the FAQ — is reviewsData.

reviewsData.test.ts pins the join: the featured slug and every shelf entry must resolve to a real non-draft post with that category, the shared four-step band must have exactly four steps and each step’s icon name must exist in the icon registry, and the make columns must sum to the headline figure they claim. Those are the invariants a section silently depends on, so they are checked rather than remembered.

The collection page

/collections/best-used-automatics-under-30k/ is a single editorial article built around six record picks. collectionsData holds the hero, the method, the picks, the at-a-glance table, the sibling collections and the editor’s note; the picks themselves are refs into carsData, so a pick’s price, photo and spec come from the record rather than being retyped. Change the record and the collection follows.

It is one hand-written route rather than a dynamic one, because there is one collection. When there are several, the shape to reach for is [collection].astro over a keyed data module — exactly what [info].astro already does.

Specialists

Two pages: a category page at /specialists/classic-restoration/ and a city page under it at /specialists/classic-restoration/london/. The category page ranks eight rated firms, explains the verification process and lists cities and trades through the shared IndexColumns band. The city page is a filterable browse over the same eight records.

Both read specialistsData.json.ts, and both are honest about being one branch of a tree that does not exist yet — the main nav points “Specialists” at the one live category page, and the source notes to repoint it when a hub lands.

The twelve info pages

/about/, /careers/, /contact/, /cookies/, /dealers/, /guides/, /history-checks/, /leasing/, /makes/, /part-exchange/, /press/ and /price-index/ are all one route: src/pages/[info].astro enumerates the keys of infoData.json.ts and renders each through the shared LegalArticle section with its own title, description and breadcrumb schema.

They exist because the footer promised them. footerLinks.test.ts walks every footer-column and legal href and asserts it resolves — either to a real file under src/pages/ or to an infoData key — which is the check that would have caught the thirteen footer links that used to 404 by design. Adding a footer link now means adding its destination, or the test fails.

NEXT STEPAnalysis & Blog