Astro template for structured knowledge sites with external content support, localized routes, reusable section renderers, and a bundled starter content root.
This repository is for people who want to publish more than a blog.
Use it when you want:
The code-level branding stays generic on purpose. The public positioning of this repo is specific.
22[email protected]Node 22 is the canonical runtime for local setup, CI, and deployment examples this quarter.
The GIF below shows the official path:
pnpm init:template
The screenshots below come from seniorpath.pro, the advanced live example built on top of this shell.
collections.manifest.json is the interface between the shell and the content source.examples/starter-content, then can graduate to a dedicated content repo without changing the shell model.| Good fit | Not a fit |
|---|---|
| knowledge sites with articles, tracks, concepts, glossary, and practice | personal landing pages with little or no content structure |
| teams that want shell and content separated | CMS-first projects that expect live in-browser editing |
| projects that need localized routes and section labels | projects that only need one flat blog index |
| static publishing flows with clear build-time content inputs | highly dynamic apps that depend on runtime content storage |
Use this sequence as the default adoption path for the repo.
pnpm install
pnpm init:template
pnpm verify:starter
pnpm dev
pnpm verify still works and is currently an alias for pnpm verify:starter.
pnpm init:content-repo ../my-content
pnpm init:template --content-root ../my-content
SITE_CONTENT_DIR=../my-content pnpm verify:external
That keeps the contract the same while moving editorial content into its own repository.
Use starter mode when you want the fastest local boot or a public demo deployment with no extra repository.
The shell falls back to examples/starter-content.
pnpm verify:starter always pins that bundled content root, even if your local config points somewhere else.
pnpm init:template
pnpm verify:starter
pnpm dev
Use external mode when the app shell and the editorial content should evolve independently.
pnpm init:content-repo ../your-content-repo
pnpm init:template --content-root ../your-content-repo
SITE_CONTENT_DIR=../your-content-repo pnpm verify:external
If .local/content-source.json is still the untouched starter bootstrap, pnpm init:template --content-root ... upgrades it to the external path for you.
Create .local/content-source.json:
{
"contentRoot": "../your-content-repo"
}
or use an environment variable:
SITE_CONTENT_DIR=/absolute/path/to/your-content-repo pnpm dev
Resolution order stays:
SITE_CONTENT_DIR.local/content-source.jsonexamples/starter-contentMore detail: docs/external-content.md
Your first real customization is done when all of these are true:
PUBLIC_SITE_NAME matches your productPUBLIC_SITE_URL matches your domainPUBLIC_STORAGE_NAMESPACE matches your projectcollections.manifest.json uses your section labels and route slugspnpm verify:starter passes for starter modeSITE_CONTENT_DIR=../your-content-repo pnpm verify:external passes for external modeThe public shell example is seniorpath.pro. Treat it as an advanced implementation of this template, not as the default branding.
| Command | Purpose |
|---|---|
pnpm init:template |
create ignored local setup files for the shell repo |
pnpm init:template --content-root ../repo |
create ignored local setup files and point to an external content repo |
pnpm init:content-repo ../repo |
scaffold a minimal external editorial repo |
pnpm verify:starter |
validate the shell with bundled starter content |
pnpm verify:external |
validate the shell against a configured external content repo |
pnpm perf:smoke |
check static build budgets and critical HTML output after a build |
pnpm docs:smoke |
validate local doc links and referenced assets |
These are the currently supported public env vars.
| Variable | Required | When used | Notes |
|---|---|---|---|
PUBLIC_SITE_NAME |
optional | always | visible site name override |
PUBLIC_SITE_DESCRIPTION |
optional | always | meta description override |
PUBLIC_SITE_URL |
recommended in production | always | used for canonical URLs, sitemap, and feed metadata |
PUBLIC_STORAGE_NAMESPACE |
optional | always | browser storage namespace |
PUBLIC_APP_URL |
optional | only if you link to a separate practice app | defaults to /app when unset |
PUBLIC_LEGAL_OWNER_NAME |
optional | only if you publish legal pages with real operator info | falls back to template copy |
PUBLIC_LEGAL_OWNER_LOCATION |
optional | same as above | falls back to template copy |
PUBLIC_GOVERNING_LAW |
optional | same as above | falls back to template copy |
PUBLIC_GOVERNING_VENUE |
optional | same as above | falls back to template copy |
PUBLIC_LEGAL_EMAIL |
optional | same as above | falls back to template copy |
PUBLIC_SUPPORT_EMAIL |
optional | same as above | falls back to template copy |
PUBLIC_NEWSLETTER_URL |
optional | only when newsletter is enabled in brand.config.ts |
newsletter stays off by default |
PUBLIC_OBSERVABILITY_SCRIPT_SRC |
optional | only when you want to inject a provider script without hard-coding a vendor | renders one async/defer script tag |
PUBLIC_OBSERVABILITY_SCRIPT_DATA_JSON |
optional | same as above | JSON object rendered as script attributes such as data-* |
PUBLIC_CSP_SCRIPT_SRC |
optional | only when you add third-party scripts | space-separated origins appended to generated CSP |
PUBLIC_CSP_STYLE_SRC |
optional | only when you add third-party stylesheets | space-separated origins appended to generated CSP |
PUBLIC_CSP_FONT_SRC |
optional | only when you add third-party font origins | space-separated origins appended to generated CSP |
PUBLIC_CSP_IMG_SRC |
optional | only when you add third-party image origins | space-separated origins appended to generated CSP |
PUBLIC_CSP_CONNECT_SRC |
optional | only when you add third-party APIs or analytics beacons | space-separated origins appended to generated CSP |
PUBLIC_CSP_FRAME_SRC |
optional | only when you add third-party embeds | space-separated origins appended to generated CSP |
PUBLIC_CSP_FORM_ACTION |
optional | only when forms submit to third parties | space-separated origins appended to generated CSP |
PUBLIC_CSP_WORKER_SRC |
optional | only when worker origins need expansion beyond the default | space-separated origins appended to generated CSP |
PUBLIC_GISCUS_REPO |
only if comments are enabled | comments | comments stay off by default |
PUBLIC_GISCUS_REPO_ID |
only if comments are enabled | comments | required with Giscus |
PUBLIC_GISCUS_CATEGORY |
only if comments are enabled | comments | required with Giscus |
PUBLIC_GISCUS_CATEGORY_ID |
only if comments are enabled | comments | required with Giscus |
PUBLIC_GISCUS_THEME |
optional | comments | defaults to app |
PUBLIC_GISCUS_EMIT_METADATA |
optional | comments | defaults to 0 |
PUBLIC_GISCUS_INPUT_POSITION |
optional | comments | defaults to bottom |
PUBLIC_GISCUS_MAPPING |
optional | comments | defaults to pathname |
PUBLIC_GISCUS_REACTIONS_ENABLED |
optional | comments | defaults to 1 |
PUBLIC_GISCUS_STRICT |
optional | comments | defaults to 0 |
newsletter is intentionally offline until you enable the feature and set PUBLIC_NEWSLETTER_URL.
Author byline defaults now live in apps/site/src/brand/brand.config.ts, so name, role, and avatar can move with the shell brand instead of staying hard-coded in UI components.
Dependency update PRs are now automated through .github/dependabot.yml for the pnpm workspace and GitHub Actions.
flowchart LR
Shell["Shell repo<br/>astro-knowledge-site-template"]
Starter["Bundled starter<br/>examples/starter-content"]
RepoTemplate["Content repo template<br/>templates/content-repo"]
External["External editorial repo<br/>your-content-repo"]
Local["Local config<br/>.local/content-source.json"]
Env["Env override<br/>SITE_CONTENT_DIR"]
Synced["Synced content<br/>apps/site/.content"]
Build["Astro dev / build / preview / verify"]
Shell --> Starter
Shell --> RepoTemplate
RepoTemplate --> External
Local --> External
Env --> External
Starter --> Synced
External --> Synced
Synced --> Build
apps/site — Astro app, routes, layouts, brand defaults, sync scriptspackages/content — shared helpers used by the shellexamples/starter-content — runnable starter content root for clean clonestemplates/content-repo — scaffold for a separate editorial repositorydocs — rebrand, deploy, content-repo, architecture, and FAQ guidesscripts — bootstrap, verification, and smoke-test entrypointsv0.xv0.x means the repo is active, but some internal details can still movecollections.manifest.json is the primary stable contract between shell and contentCHANGELOG.mdThe public acceptance paths for this repo are now:
pnpm init:template
pnpm verify:starter
pnpm docs:smoke
and for a separate editorial repository:
pnpm init:content-repo ../sample-content
SITE_CONTENT_DIR=../sample-content pnpm verify:external