The Nx documentation site built with Astro and Starlight, featuring advanced content management through Markdoc and dynamic plugin documentation generation.
This documentation site leverages Astro's static site generation capabilities with Starlight for documentation-specific features. The architecture consists of:
- Astro - Static site generator with island architecture
- Starlight - Documentation theme with built-in navigation, search, and i18n
- React - For implementing UI components
- Netlify - Deployment and hosting
- Markdoc with custom tags for rich content such as videos, graphs, etc.
- TailwindCSS for styling in Astro and React components
- Dynamic API documentation generation from Nx packages and CLI commands
- Community plugin registry
When creating or reorganizing documentation, follow these 5 principles to determine where content belongs.
- Concept: Don't overwhelm the user. Reveal complexity only as they advance in their journey.
- The Test: Is this for the First 30 Minutes (Getting Started), the First 30 Days (Features), or Forever (Reference)?
- Concept: Items in a list must be of the same "type" (noun, verb, or concept) to reduce cognitive load.
- The Test: Does this list mix Concepts (Mental Model), Tasks (Update Nx), and Products (React)? If yes, split it.
- Concept: Separate Learning (Narrative/Guides) from Looking Up (Reference/API).
- The Test: Is the user here to learn a workflow (Guide) or look up a flag syntax (Reference)?
- Concept: Distinguish Architecture from Features to keep "Core Concepts" pure.
- The Test: Can I explain this using only a pen and paper?
- Yes: It goes in How Nx Works (Architecture).
- No (I need a terminal): It goes in Platform Features (Feature).
- Concept: Distinguish Platform features from Ecosystem tools to prevent "Features" from becoming a junk drawer.
- The Test: Does this feature apply to EVERY user (e.g., Caching, Agents)?
- Yes: Platform Features.
- No (Only React users): Technologies.
The sidebar has 4 top-level sections that follow the user journey:
- Getting Started - Essential setup, tutorials, and core concepts (How Nx Works, Platform Features)
- Technologies - Framework and tool-specific guides (React, Angular, Node, build tools, test tools)
- Knowledge Base - Recipes, troubleshooting, and topic-specific guides
- Reference - Exhaustive facts, no narrative (CLI commands, configuration, API docs)
astro-docs/
├── src/
│ ├── assets/ # Images and static assets to be optimized by Astro
│ │ ├── nx/ # Nx branding assets
│ │ ├── nx-cloud/ # Nx Cloud assets
│ │ └── nx-console/ # Nx Console assets
│ ├── components/ # React and Astro components
│ │ ├── layout/ # Layout components (e.g. Sidebar)
│ │ ├── markdoc/ # Markdoc tag components
│ │ └── utils/ # Utility functions
│ ├── content/ # Documentation content
│ │ ├── banner.json # Banner collection (generated by prebuild-banner)
│ │ ├── docs/ # Main documentation files (.mdoc, .mdx)
│ │ └── approved-community-plugins.json # Powers plugin registry
│ ├── pages/ # Dynamic pages and routes (e.g. devkit)
│ ├── plugins/ # Content loaders and plugins
│ │ ├── *.loader.ts # Dynamic content loaders (e.g. CLI commands and API docs generation)
│ │ └── utils/ # Plugin utilities
│ └── styles/ # Global styles
├── public/ # Static assets not to be optimized by Astro (fonts, robots.txt)
├── astro.config.mjs # Astro configuration
├── markdoc.config.mjs # Markdoc tags configuration
├── sidebar.mts # Sidebar structure definition
└── package.json
The site uses custom content loaders to dynamically generate documentation:
- PluginLoader (
plugin.loader.ts) - Generates official plugin documentation (generators, executors, migrations) - CommunityPluginsLoader (
community-plugins.loader.ts) - Generates data for plugin registry (e.g. GitHub stars, npm downloads) - NxReferencePackagesLoader (
nx-reference-packages.loader.ts) - Generated data for CNW, Devkit, nx cli (e.g. nx core related things)
-
Regular Documentation (
src/content/docs/)- Written in
.mdoc(Markdoc) or.mdx(MDX) format - Organized by sections: getting-started, concepts, guides, api
- File-based routing (filename = URL path)
- Written in
-
Dynamic Plugin Documentation
- Auto-generated from Nx packages
- Includes generators, executors, and migrations
- Updated during build process
- Note: Requires a rebuild and restart to reflect changes
-
CLI Documentation
- Auto-generated from Nx CLI commands
- Parsed from actual CLI implementation
- Note: Requires a rebuild and restart to reflect changes
The site includes custom Markdoc tags for rich content.
Note: Starlight supports many Markdown and Markdoc features, such as code blocks, asides, etc.
- https://starlight.astro.build/components/using-components/#using-a-component-in-markdoc
- https://starlight.astro.build/guides/authoring-content
{% aside %}- Highlighted information boxes{% cardgrid %},{% card|linkcard %}- Card layouts{% tabs %},{% tabitem label="some-label" %}- Tab layouts- Use the
syncKeyso tabs are auto switched to the users preference if it makes sense. - e.g.
{% tabs syncKey="package-manager" %}
- Use the
{% graph %}- Interactive project/task graph visualization{% project_details %}- Project configuration viewer
{% youtube %}- YouTube video embeds{% video_player %}- Custom video player{% iframe %}- Generic iframe embeds
{% github_repository %}- GitHub repo cards{% stackblitz_button %}- StackBlitz demo launcher{% install_nx_console %}- IDE extension installer
{% badge %}- Status/label pills{% metrics %}- Metrics display{% testimonial %}- Customer testimonials
# Install dependencies and link workspace packages
# This will build Nx packages as well for API docs
nx serve astro-docs
# Or run astro dev directly
# This will not build Nx packages
cd astro-docs
npx astro dev
# Custom ports (useful for AI agents with git worktrees)
npx astro dev --port 3000- Create
.mdocfile insrc/content/docs/ - Add frontmatter with title and description
- Use Markdoc tags for rich content
- File location determines URL structure
Example:
---
title: 'My New Guide'
description: 'Learn how to use this feature'
---
# Introduction
{% aside type="note" title="Important" %}
This is a note about the feature.
{% /aside %}- Create Astro component in
src/components/markdoc/ - (Optional) Create React component for more complex components, or ones that need to be shared with blog or non-docs pages
- Register in
markdoc.config.mjs - Define attributes and validation
Plugin documentation is auto-generated during build. To update:
- Make changes to the plugin's schema/implementation
- Run the build process
- The loader will automatically fetch and generate updated docs
The sidebar structure is defined in sidebar.mts. To add new sections:
export const sidebar = [
{
label: 'Section Name',
items: [
{
label: 'Page Title',
link: 'path/to/page',
},
// Nested sections
{
label: 'Subsection',
collapsed: true,
items: [...]
}
]
}
];Note there is a special case for sidebar items appearing in the sidebar. Such as the
Referencesection which is handled via the[sidebar-reference-updater](./src/plugins/sidebar-reference-updater.middleware.ts)middleware.
- Uses Tailwind CSS v4 with Vite plugin
- Global styles in
src/styles/global.css - Component-specific styles use Tailwind utilities
- Dark/light mode support built into Starlight and customized in
global.css
- Site configuration
- Integration setup (React, Markdoc, Starlight)
- Vite plugins
- Build options
- Custom tag definitions
- Attribute validation
- Component mappings
- Navigation structure
- Section organization
- Dynamic content injection points
The floating banner promotes events/webinars. It's fetched at build time from a Framer CMS page and stored as an Astro content collection.
Set BANNER_URL to point to a Framer page that renders banner JSON:
BANNER_URL=https://your-framer-site.framer.app/api/banners/main
The Framer page should render JSON inside a <pre> tag:
{
"title": "Event Title",
"description": "Event description",
"primaryCtaUrl": "https://...",
"primaryCtaText": "Learn More",
"secondaryCtaUrl": "",
"secondaryCtaText": "",
"enabled": true,
"activeUntil": "2025-12-31T00:00:00.000Z"
}| Field | Type | Required | Description |
|---|---|---|---|
title |
string | Yes | Banner headline |
description |
string | Yes | Banner body text |
primaryCtaUrl |
string | Yes | Primary button URL |
primaryCtaText |
string | Yes | Primary button text |
secondaryCtaUrl |
string | No | Secondary button URL |
secondaryCtaText |
string | No | Secondary button text |
enabled |
boolean | Yes | Show/hide the banner |
activeUntil |
ISO 8601 | No | Auto-hide after this date |
- Banner is fetched during
prebuild-bannertarget and saved tosrc/content/banner.jsonas a collection (array) - Uses Astro content collection with
file()loader and schema validation - Requires rebuild/redeploy to update the banner
- Users can dismiss the banner (stored in localStorage)
- If
enabledisfalseoractiveUntilhas passed, the banner won't show - If
BANNER_URLis not set, an empty collection is generated
When a new major Nx version is released (or about to be released), create a versioned snapshot of the docs site so the previous version remains accessible at {major}.nx.dev (e.g. 22.nx.dev).
node ./scripts/create-versioned-docs.mts 22This will:
- Fetch tags from origin, find the latest stable release for that major (e.g.
22.6.4) - Checkout that tag, install deps, and build the docs site
- Create an orphan git branch
22containing only the pre-built static site plus minimal scaffolding (rootpackage.json,nx.json,pnpm-lock.yaml,netlify.toml, and a no-opnx-devproject) so Netlify's configured build command succeeds instantly - Return to your original branch
If no stable tags exist for that major version, it builds from the current branch.
For Nx 21+, the script builds astro-docs (Astro/Starlight). For legacy Nx 18–20, it builds nx-dev (Next.js with static export) — this path will be removed once those versions are no longer maintained.
--force— overwrite an existing local/remote{major}branch--redirect-to-prod— skip the build and produce a branch that 301s every path tohttps://nx.dev/docs. Used to retire an old versioned subdomain (e.g.16.nx.dev) without maintaining its docs
# Retire an old versioned site
node ./scripts/create-versioned-docs.mts 16 --redirect-to-prodgit push -f origin 22Versioned sites are served via Netlify branch deploys of the main nx-dev Netlify site, with custom domains managed in Squarespace.
- Netlify — each
{major}branch is deployed as a branch deploy of thenx-devsite. The branch's rootnetlify.tomloverrides the UI build settings so Netlify serves the pre-built static files (no rebuild, no@netlify/plugin-nextjs). Add the branch to the site's branch deploy allowlist, then add{major}.nx.devas a domain alias pointing at the branch deploy - Squarespace — DNS for
nx.devis managed in Squarespace. Add a CNAME for{major}pointing at the Netlify branch deploy hostname