Picking Up Where the Manifest Leaves Off Copied!
The previous article explained how the project builds a manifest from the journal collection.
The manifest converts raw MDX files into structured data.
The next stage is rendering.
Astro takes that structured data and turns it into static pages. Every journal article becomes a real page at build time.
The core piece responsible for that is the dynamic route.
src/pages/thejournal/[...slug].astroThis file handles every journal page.
How the Dynamic Route Works Copied!
The file[...slug].astroacts as a catch-all route.
Astro uses it to render every entry under/thejournal.
Example URLs.
thejournal/architecture
thejournal/architecture/astro-content
thejournal/javascript/closuresAstro resolves these paths through a build step calledgetStaticPaths.
A simplified version looks like this.
export async function getStaticPaths() {
const paths = Object.values(entryManifest).map((entry) => ({
params: { slug: entry.slug.split("/") },
}));
return paths;
}This function tells Astro which pages must exist.
Each entry in the manifest becomes a static route.
Build output looks like this.
build/
│
├── thejournal
│ ├── architecture
│ │ ├── index.html
│ │ └── astro-content/index.html
│ │
│ └── javascript
│ └── closures/index.htmlOnce Astro knows the route, the page component loads the entry context.
How the Entry Binding Happens Copied!
The page receives the slug parameter.
Example.
const slug = Astro.params.slug?.join("/") ?? "";That slug represents the path inside the journal.
The page resolves the entry using the helper described in the previous article.
const [entry, vault] = getContextFromPath(slug);At this point the page has two things.
- EntryContext
- VaultContext
URL
│
▼
Slug parameter
│
▼
getContextFromPath()
│
├── EntryContext
└── VaultContextEntryContext contains the article metadata.
VaultContext contains the navigation structure.
The MDX component is then rendered.
const { Content } = await entry.render();The article now becomes HTML during the build.
The Header Component Copied!
Every journal page uses a shared header component.
Its job is simple.
It displays metadata from the MDX frontmatter.
Typical fields.
title
description
pubDate
tags
readTimeExample usage inside the page.
<JournalHeader
title={entry.title}
description={entry.description}
pubDate={entry.pubDate}
tags={entry.tags}
/>EntryContext
│
├── title
├── description
├── pubDate
└── tags
│
▼
Header Component
│
▼
Rendered HTMLBecause the data already exists in the manifest, the header renders instantly.
No runtime requests.
MDX Metadata and Its Parameters Copied!
Every journal entry contains frontmatter.
Example.
---
title: Astro Content Collections
description: How Astro organizes content
pubDate: 2025-01-01
tags: [astro, content]
order: 2
github: https://github.com/example
---These values are validated by the schema defined in the collection.
Fields.
- title
- description
- pubDate
- tags
- order
- github
- image
The important field for navigation isorder.
How the Order Parameter Works Copied!
Entries inside a vault follow theorderfield.
Lower values appear first.
Example vault.
architecture
│
├── index.mdx order 1
├── astro-content order 2
└── routing order 3Result.
1 Architecture Overview
2 Astro Content
3 RoutingSubfolders follow the same rule.
Example.
architecture
│
├── index.mdx
├── rendering
│ ├── index.mdx
│ ├── hydration.mdx
│ └── islands.mdx
└── routing
├── index.mdx
└── dynamic-routes.mdxImportant detail.
Theindex.mdxinside a subfolder determines where the entire folder appears in the parent vault.
Parent Vault
│
├── Entry A
├── Rendering Vault
│ ├── index
│ ├── hydration
│ └── islands
│
└── Routing Vault
├── index
└── dynamic-routesSo ordering works in two levels.
- Entry order inside a vault
- Subfolder position based on its index file
Building the Table of Contents Copied!
Vault pages contain a table of contents.
The table is generated from the vault structure.
Example vault tree.
Architecture
│
├── Overview
├── Astro Content
├── Rendering
│ ├── Hydration
│ └── Islands
│
└── Routing
└── Dynamic RoutesThis tree already exists insidevaultsManifest.
The page component simply renders it.
<TableOfContents vault={vault} current={entry.id} />Highlighting the Current Page Copied!
The table highlights the current article.
The logic compares the entry id.
Simplified example.
const isActive = item.id === currentEntryId;If the ids match, the item receives an active style.
Table Node
│
├── item.id
└── currentEntryId
│
▼
Compare
│
▼
Apply active classThe result is a navigation tree that always reflects the current page.
Navigation Buttons Between Articles Copied!
Each article shows previous and next buttons.
Those links were already created during the manifest stage.
Each EntryContext contains.
previous
nextThe page simply renders them.
<ArticleNavigation
previous={entry.previous}
next={entry.next}
/>Entry A
│
├── previous null
└── next Entry B
Entry B
│
├── previous Entry A
└── next Entry CBecause these links exist at build time, the page does no computation.
Why Everything Happens at Build Time Copied!
The key design choice of this system is precomputation.
The manifest builds structure.
Astro builds the pages.
Navigation, ordering, and the table of contents already exist when the site is deployed.
Build flow.
MDX Files
│
▼
Astro Collection
│
▼
Manifest Builder
│
├── entryManifest
└── vaultsManifest
│
▼
Dynamic Route Rendering
│
▼
Static HTML PagesFrom the user perspective the site behaves like a simple static page.
No client side script builds navigation.
No runtime database queries.
Everything is ready before the page loads.
That is why the journal feels fast.
