Back to Nx

Nx Documentation Site

astro-docs/README.md

22.7.113.4 KB
Original Source

Nx Documentation Site

The Nx documentation site built with Astro and Starlight, featuring advanced content management through Markdoc and dynamic plugin documentation generation.

Architecture Overview

This documentation site leverages Astro's static site generation capabilities with Starlight for documentation-specific features. The architecture consists of:

Core Technologies

  • 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

Key Features

  • 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

Information Architecture Principles

When creating or reorganizing documentation, follow these 5 principles to determine where content belongs.

1. Progressive Disclosure (The "Journey" Rule)

  • 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)?

2. Category Homogeneity (The "Scan" Rule)

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

3. Type-Based Navigation (The "Intent" Rule)

  • 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)?

4. The Pen & Paper Test (The "Theory" Rule)

  • 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).

5. Universal vs. Specific (The "Placement" Rule)

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

  1. Getting Started - Essential setup, tutorials, and core concepts (How Nx Works, Platform Features)
  2. Technologies - Framework and tool-specific guides (React, Angular, Node, build tools, test tools)
  3. Knowledge Base - Recipes, troubleshooting, and topic-specific guides
  4. Reference - Exhaustive facts, no narrative (CLI commands, configuration, API docs)

Project Structure

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

Plugins and Loaders

Content Loaders

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)

Content Management

Content Types

  1. 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)
  2. 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
  3. CLI Documentation

    • Auto-generated from Nx CLI commands
    • Parsed from actual CLI implementation
    • Note: Requires a rebuild and restart to reflect changes

Markdoc Tags

The site includes custom Markdoc tags for rich content.

Note: Starlight supports many Markdown and Markdoc features, such as code blocks, asides, etc.

Layout & Organization

  • {% aside %} - Highlighted information boxes
  • {% cardgrid %}, {% card|linkcard %} - Card layouts
  • {% tabs %}, {% tabitem label="some-label" %} - Tab layouts
    • Use the syncKey so tabs are auto switched to the users preference if it makes sense.
    • e.g. {% tabs syncKey="package-manager" %}

Interactive Components

  • {% graph %} - Interactive project/task graph visualization
  • {% project_details %} - Project configuration viewer

Media & Embeds

  • {% youtube %} - YouTube video embeds
  • {% video_player %} - Custom video player
  • {% iframe %} - Generic iframe embeds

Developer Tools

  • {% github_repository %} - GitHub repo cards
  • {% stackblitz_button %} - StackBlitz demo launcher
  • {% install_nx_console %} - IDE extension installer

Content Enhancement

  • {% badge %} - Status/label pills
  • {% metrics %} - Metrics display
  • {% testimonial %} - Customer testimonials

Development Workflow

Getting Started

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

Adding New Content

Regular Documentation

  1. Create .mdoc file in src/content/docs/
  2. Add frontmatter with title and description
  3. Use Markdoc tags for rich content
  4. File location determines URL structure

Example:

markdown
---
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 %}

Adding Custom Markdoc Tags

  1. Create Astro component in src/components/markdoc/
  2. (Optional) Create React component for more complex components, or ones that need to be shared with blog or non-docs pages
  3. Register in markdoc.config.mjs
  4. Define attributes and validation

Updating Plugin Documentation

Plugin documentation is auto-generated during build. To update:

  1. Make changes to the plugin's schema/implementation
  2. Run the build process
  3. The loader will automatically fetch and generate updated docs

The sidebar structure is defined in sidebar.mts. To add new sections:

javascript
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 Reference section which is handled via the [sidebar-reference-updater](./src/plugins/sidebar-reference-updater.middleware.ts) middleware.

Styling and Theming

  • 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

Configuration Files

astro.config.mjs

  • Site configuration
  • Integration setup (React, Markdoc, Starlight)
  • Vite plugins
  • Build options

markdoc.config.mjs

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

Setup

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:

json
{
  "title": "Event Title",
  "description": "Event description",
  "primaryCtaUrl": "https://...",
  "primaryCtaText": "Learn More",
  "secondaryCtaUrl": "",
  "secondaryCtaText": "",
  "enabled": true,
  "activeUntil": "2025-12-31T00:00:00.000Z"
}

Schema

FieldTypeRequiredDescription
titlestringYesBanner headline
descriptionstringYesBanner body text
primaryCtaUrlstringYesPrimary button URL
primaryCtaTextstringYesPrimary button text
secondaryCtaUrlstringNoSecondary button URL
secondaryCtaTextstringNoSecondary button text
enabledbooleanYesShow/hide the banner
activeUntilISO 8601NoAuto-hide after this date

Behavior

  • Banner is fetched during prebuild-banner target and saved to src/content/banner.json as 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 enabled is false or activeUntil has passed, the banner won't show
  • If BANNER_URL is not set, an empty collection is generated

Versioned Docs

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

Creating a Version Snapshot

bash
node ./scripts/create-versioned-docs.mts 22

This will:

  1. Fetch tags from origin, find the latest stable release for that major (e.g. 22.6.4)
  2. Checkout that tag, install deps, and build the docs site
  3. Create an orphan git branch 22 containing only the pre-built static site plus minimal scaffolding (root package.json, nx.json, pnpm-lock.yaml, netlify.toml, and a no-op nx-dev project) so Netlify's configured build command succeeds instantly
  4. 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.

Flags

  • --force — overwrite an existing local/remote {major} branch
  • --redirect-to-prod — skip the build and produce a branch that 301s every path to https://nx.dev/docs. Used to retire an old versioned subdomain (e.g. 16.nx.dev) without maintaining its docs
bash
# Retire an old versioned site
node ./scripts/create-versioned-docs.mts 16 --redirect-to-prod

Pushing the Branch

bash
git push -f origin 22

Deployment Setup

Versioned 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 the nx-dev site. The branch's root netlify.toml overrides 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.dev as a domain alias pointing at the branch deploy
  • Squarespace — DNS for nx.dev is managed in Squarespace. Add a CNAME for {major} pointing at the Netlify branch deploy hostname