---
name: add-docs-page
description: Add, move, rename, or delete a page on the LangChain docs site. Covers choosing the source directory, writing frontmatter, placing the entry in src/docs.json navigation, adding redirects, and verifying with the lint and broken-link gates. Use when asked to add a new doc or page, move or rename a page, put something in the nav, or add a redirect.
license: MIT
metadata:
  author: langchain
  version: "1.0"
---

# Add or move a docs page

A page is not finished when the MDX file exists. It also needs a navigation
entry, and a move or deletion needs a redirect. CI enforces both.

Read `AGENTS.md` for the navigation map, the style guide, and the frontmatter
rules. This skill covers the procedure around them and does not repeat them.

## Step 1. Choose the source directory

Use the "Source directory summary" table in `AGENTS.md` to go from subject to
directory. Two traps:

- Directory names do not match navigation names. `src/langsmith/fleet/` appears
  as "No-code agents"; `src/langsmith/managed-deep-agents*.mdx` appears under
  Build, not under a LangSmith menu.
- Lifecycle menus mix products. Build draws from both `src/oss/` and
  `src/langsmith/`; Test, Deploy, and Monitor all draw from `src/langsmith/`.

Never write to `build/`. It is Mintlify output, regenerated by `make build`.

## Step 2. Write the page

Required frontmatter:

```yaml
---
title: Clear, concise page title
description: SEO summary with no markdown, no links, and no backticks
---
```

For OSS pages that differ by language, use `:::python` and `:::js` fences in one
file rather than writing two files. The build pipeline emits both versions.

## Step 3. Add the navigation entry

Navigation lives in `src/docs.json` under `navigation.products`. There are two
products: `products[0]` is AGENT DEVELOPMENT LIFECYCLE (Home, Build, Test,
Deploy, Monitor) and `products[1]` is PRODUCTS AND SETUP (LangSmith setup, LLM
Gateway, No-code agents, Engine, Deep Agents Code).

Each menu item is addressed by its `item` key, then nests one of two ways:

- `menu[].tabs[].pages[]` for most menu items.
- `menu[].dropdowns[].tabs[].pages[]` for Build, which has Python and
  TypeScript dropdowns.

A `pages` array holds page-path strings and `{"group": ..., "pages": [...]}`
objects, nested to any depth.

Three rules:

1. **A language-versioned OSS page needs two entries.** Build page paths carry
   the language segment (`oss/python/deepagents/overview` and
   `oss/javascript/deepagents/overview`), so one new page means one entry in the
   Python dropdown and one in the TypeScript dropdown. Omitting the TypeScript
   entry is the most common miss.
2. **Page paths omit the `src/` prefix and the file extension.**
   `src/langsmith/sandboxes.mdx` is `"langsmith/sandboxes"`.
3. **A new group leads with an index page:**
   `"pages": ["group/index", "group/page"]`.

Integration pages are the exception. Add them to the component's `index.mdx`
instead, and touch `docs.json` only when creating a brand-new component group.

## Step 4. Add redirects for a move, rename, or deletion

For a move or rename of a file that was already on `main`, run the repo's mover first. It rewrites cross-references
across the corpus, which hand-editing misses:

```bash
uv run docs mv src/langsmith/old-name.mdx src/langsmith/new-name.mdx --dry-run
```

Drop `--dry-run` once the preview looks right. Then add the redirect to the
`redirects` array in `src/docs.json`:

```json
{ "source": "/langsmith/old-name", "destination": "/langsmith/new-name" }
```

Redirect paths are site paths and start with `/`. A language-versioned page
needs a redirect per language, plus one for the unversioned path if the old URL
had one.

`scripts/check_removed_pages_redirects.py` runs in CI and fails the PR when a
page leaves the navigation and its source file is gone with no redirect. The
same script fails when `docs.json` names a page whose file does not exist, so a
typo in a page path is caught there rather than at build time.

## Step 4b. Two traps that fail silently

### Extract a snippet once a block appears on three pages

`AGENTS.md` covers how to add a snippet. The rule for **when**: the same block
repeated on three or more pages becomes one file under `src/snippets/`. A status
callout duplicated across a page family means the wording change that retires it
is an edit to every page in the family, and one will be missed.

Verify a new snippet reaches the build. The pipeline rewrites snippet imports to
language-specific paths, so `/snippets/langsmith/x.mdx` becomes
`/snippets/python/langsmith/x.mdx` in the output, and a missing target renders as
nothing at all rather than as an error:

```bash
ls build/snippets/python/langsmith/<name>.mdx build/snippets/javascript/langsmith/<name>.mdx
```

### Editing a heading moves its anchor

A heading's slug is derived from its text, so rewording one silently breaks every
`#anchor` link pointing at it, including links from other pages and entries in
`src/docs.json`. Grep before editing:

```bash
grep -rn 'use-with-the-langsmith-gateway' src/ --include=*.mdx --include=*.json
```

Changing only capitalization is safe, because slugs are lowercased. Changing a
word is not, and needs either a reworded inbound link or a redirect.
`<Step>` and `<Accordion>` accept an explicit `id`, which is how to keep a
landing spot that is no longer a heading.

## Step 5. Verify

Run all three, in this order:

```bash
make lint_prose FILES="src/path/to/page.mdx"
make build
make broken-links-with-anchors
```

When `make build` fails with `Required uv version >=0.9.26 does not match the
running version`, the local `uv` has drifted from the one `pyproject.toml`
expects. Run the pipeline directly rather than working around the build:

```bash
PYTHONPATH="$(pwd)" .venv/bin/python -m pipeline build
```

`make broken-links-with-anchors` depends on `build`, so it fails the same way.
Its link-check half runs on its own once the build output exists:

```bash
cd build && mint broken-links --check-anchors | tee /tmp/bl.txt
cd .. && python3 scripts/filter_mint_broken_links.py --check-anchors --input /tmp/bl.txt
```

Read `make broken-links-with-anchors` output by skipping to the `⎿` lines. Those are the only
real failures. A bare filename with no indented lines beneath it is an
OpenAPI-generated page that exists at deploy time but not locally.

Fix every Vale finding. CI blocks on `lint_prose`, and its most common failure is
a spaced em dash (`word — word` must be `word—word`).

## Step 6. Review the prose

Once the edit is complete and before committing, invoke the `docs-review` skill
on the files this pass changed. It runs in working-tree mode, so it needs no
checkout, and it covers the style-guide rules Vale cannot see: passive voice,
filler, product versus common noun capitalization, structure conventions, and
link text.

Run it on finished edits only. A review of a half-written section produces
findings that go stale as soon as writing resumes.

Skip this step for a change too small to have prose in it, such as a pure
`docs.json` reorder or a redirect-only fix.

## Checklist

- [ ] File in the directory the source-directory table names, not `build/`.
- [ ] Frontmatter present, `description` free of markdown.
- [ ] `src/docs.json` entry in the right product, menu item, tab, and group.
- [ ] Both language entries added if the page is language-versioned.
- [ ] Index page first if a new group was created.
- [ ] Redirect added for every moved, renamed, or deleted path.
- [ ] Inbound `#anchor` links checked before any heading was reworded.
- [ ] A block now on three or more pages extracted to `src/snippets/`, and its
      built `python/` and `javascript/` targets confirmed to exist.
- [ ] `make lint_prose` clean, `make broken-links-with-anchors` shows no new `⎿` lines.
- [ ] `docs-review` run on the changed files, findings addressed.
