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
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