Skip to content

fix: Strip JSX components from heading anchors and TOC entries#12404

Merged
anthonyshew merged 1 commit intomainfrom
shew/strip-prerelease-badges
Mar 20, 2026
Merged

fix: Strip JSX components from heading anchors and TOC entries#12404
anthonyshew merged 1 commit intomainfrom
shew/strip-prerelease-badges

Conversation

@anthonyshew
Copy link
Copy Markdown
Contributor

Summary

  • Heading anchor IDs and TOC entries included text from inline JSX components (like <ExperimentalBadge>), producing anchors like #boundaries-experimental and cluttered TOC sidebars
  • Adds a rehype plugin (rehype-strip-heading-jsx) that runs between remarkHeading and rehypeToc in the fumadocs pipeline to strip JSX from heading IDs and TOC titles while preserving badge rendering in the heading itself
  • Removes brittle badge-specific workarounds from the link checker

How it works

The rehype plugin intercepts heading elements after remarkHeading sets IDs but before rehypeToc reads them:

  1. Rewrites the heading id without JSX text content
  2. Removes JSX elements from heading children (so rehypeToc produces clean TOC titles)
  3. Serializes the removed JSX as a data-heading-badges attribute

The HeadingWithBadges component reads this attribute and re-injects the badge components inline at render time. Badges appear in the heading but never reach the TOC or anchor ID.

Testing

Visit /docs/reference/configuration and verify:

  • Heading anchors are clean (e.g., #affectedusingtaskinputs not #affectedusingtaskinputs-pre-release)
  • TOC sidebar entries don't include badge text
  • Badges still render inline in the heading content
  • Blog posts with anchor links to badge headings still navigate correctly (/blog/turbo-2-4, /blog/turbo-2-2-0, /blog/turbo-1-9-0)

Heading IDs and TOC entries included text from inline JSX components
(e.g., ExperimentalBadge), producing polluted anchors like
#boundaries-experimental and cluttered TOC entries.

Adds a rehype plugin that runs between remarkHeading and rehypeToc to:
- Rewrite heading IDs without JSX text
- Remove JSX from heading children so rehypeToc produces clean titles
- Serialize removed JSX as a data attribute for re-injection

The HeadingWithBadges component reads the serialized data and renders
badges inline, preserving the visual appearance while keeping anchors
and TOC clean.

Also fixes the link checker to strip JSX tags from heading text during
slug extraction, removing the brittle badge-specific workarounds.
@anthonyshew anthonyshew requested a review from a team as a code owner March 20, 2026 17:55
@anthonyshew anthonyshew requested review from tknickman and removed request for a team March 20, 2026 17:55
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Mar 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
examples-basic-web Ready Ready Preview, Comment, Open in v0 Mar 20, 2026 5:56pm
examples-designsystem-docs Ready Ready Preview, Comment, Open in v0 Mar 20, 2026 5:56pm
examples-gatsby-web Ready Ready Preview, Comment, Open in v0 Mar 20, 2026 5:56pm
examples-kitchensink-blog Ready Ready Preview, Comment, Open in v0 Mar 20, 2026 5:56pm
examples-nonmonorepo Ready Ready Preview, Comment, Open in v0 Mar 20, 2026 5:56pm
examples-svelte-web Ready Ready Preview, Comment, Open in v0 Mar 20, 2026 5:56pm
examples-tailwind-web Ready Ready Preview, Comment, Open in v0 Mar 20, 2026 5:56pm
examples-vite-web Ready Ready Preview, Comment, Open in v0 Mar 20, 2026 5:56pm
turbo-site Ready Ready Preview, Comment, Open in v0 Mar 20, 2026 5:56pm
turborepo-agents Ready Ready Preview, Comment, Open in v0 Mar 20, 2026 5:56pm

@ghost ghost added the area: site Issues and improvements related to Turborepo's documentation website label Mar 20, 2026
@anthonyshew anthonyshew merged commit 3abe553 into main Mar 20, 2026
41 checks passed
@anthonyshew anthonyshew deleted the shew/strip-prerelease-badges branch March 20, 2026 19:01
github-actions Bot added a commit that referenced this pull request Mar 20, 2026
## Release v2.8.21-canary.2

Versioned docs: https://v2-8-21-canary-2.turborepo.dev

### Changes

- fix: Strip JSX components from heading anchors and TOC entries
(#12404) (`3abe553`)
- fix: Move docs app icons into app/ directory (#12403) (`ddf3918`)
- feat: Add experimental structured logging with `--json` and
`--log-file` flags (#12405) (`7ca0601`)
- release(turborepo): 2.8.21-canary.1 (#12407) (`adebb95`)
- docs: Downgrade Next.js (#12408) (`281e89b`)
- chore: Deprecate the `turbo scan` command (#12406) (`4a12c26`)

Co-authored-by: Turbobot <turbobot@vercel.com>
github-actions Bot added a commit that referenced this pull request Mar 28, 2026
## Release v2.8.21

Versioned docs: https://v2-8-21.turborepo.dev

### Changes

- release(turborepo): 2.8.20 (#12396) (`45230ec`)
- fix: Disable husky hooks in `update-examples` workflow (#12397)
(`56b79ff`)
- docs: Add link to Docker guide in prune --docker flag section (#12401)
(`e7f0db7`)
- feat: Add `global` configuration key behind
`futureFlags.globalConfiguration` (#12399) (`5f190cf`)
- chore: Update CODEOWNERS to remove /docs owner (#12402) (`3233d3a`)
- fix: Strip JSX components from heading anchors and TOC entries
(#12404) (`3abe553`)
- fix: Move docs app icons into app/ directory (#12403) (`ddf3918`)
- feat: Add experimental structured logging with `--json` and
`--log-file` flags (#12405) (`7ca0601`)
- release(turborepo): 2.8.21-canary.1 (#12407) (`adebb95`)
- docs: Downgrade Next.js (#12408) (`281e89b`)
- chore: Deprecate the `turbo scan` command (#12406) (`4a12c26`)
- release(turborepo): 2.8.21-canary.2 (#12409) (`b9ef212`)
- fix(eslint-plugin-turbo): Guard against missing tasks/pipeline in
forEachTaskDef (#12411) (`6c107c2`)
- release(turborepo): 2.8.21-canary.3 (#12413) (`a2e6635`)
- chore: Upgrade Next.js (#12415) (`b9e6174`)
- Revert "fix: Flush stale mouse tracking events from stdin during TUI
cleanup" (#12416) (`646b06e`)
- fix: Add NixOS environment variables to default passthroughs (#12417)
(`4f12c69`)
- release(turborepo): 2.8.21-canary.4 (#12419) (`19cb539`)
- fix: Resolve security vulnerabilities in `tar` and `rustls-webpki`
(#12418) (`f09b138`)
- release(turborepo): 2.8.21-canary.5 (#12420) (`8aca047`)
- docs: Promote `turbo query` from experimental to stable (#12421)
(`0692aba`)
- docs: Clarify `turbo-ignore`'s future (#12422) (`c5a8235`)
- release(turborepo): 2.8.21-canary.6 (#12423) (`3ebf536`)
- feat: Rework turbo ls to use query internals and add turbo query ls
shorthand (#12424) (`84fd6e3`)
- docs: Clarify environment variables across packages dependency
behavior (#12390) (`e44b0d8`)
- docs: Expand subpath imports example (#12412) (`a7fec57`)
- fix(examples): Update of `with-svelte` example (#11952) (`41d1b2e`)
- release(turborepo): 2.8.21-canary.7 (#12425) (`7155a67`)
- fix: Preserve source dependencies when adding workspace deps in
`turbo-gen` (#11935) (`01c56cc`)
- docs: Add Git history requirements to `turbo query affected` docs
(#12426) (`edc16d5`)
- fix: Prevent horizontal overflow from long inline code on narrow
viewports (#12428) (`a5d641b`)
- release(turborepo): 2.8.21-canary.8 (#12429) (`46814d0`)
- feat: Send git SHA and dirty hash to remote cache (#12427) (`192034a`)
- fix: Upgrade tokio to 1.47.1+ to fix pidfd_reaper panic (#12431)
(`8c25d47`)
- release(turborepo): 2.8.21-canary.9 (#12432) (`2e2f8c3`)
- fix: Use script-shell=bash for cross-platform with-shell-commands
example (#12436) (`d5c2192`)
- docs: Add AI guide to sidebar navigation (#12438) (`021d288`)
- docs: Move `experimentalObservability` into `futureFlags` section
(#12439) (`85812cc`)
- fix: Skip Unix domain sockets and other special files during file
hashing (#12445) (`eb8f75e`)
- fix: Preserve dedupePeers and unknown pnpm lockfile settings (#12443)
(`1529b92`)
- release(turborepo): 2.8.21-canary.10 (#12446) (`014111c`)
- fix: Align dry run cache status with normal run by checking caching
guards (#12448) (`48aa171`)
- release(turborepo): 2.8.21-canary.11 (#12450) (`b14aa0b`)
- fix: Resolve turbo watch hang with mixed interruptible persistent
tasks (#12449) (`326532d`)
- release(turborepo): 2.8.21-canary.12 (#12451) (`379d47b`)
- fix: Avoid `setsid()` in PTY spawn to prevent macOS Gatekeeper CPU
spikes (#12452) (`dcc9f6a`)
- release(turborepo): 2.8.21-canary.13 (#12453) (`19f46e6`)
- feat: Add `packagesFromLockfile()` NAPI binding to `@turbo/repository`
(#12454) (`c58ee79`)
- release(library): 0.0.1-canary.21 (#12455) (`3637185`)
- release(turborepo): 2.8.21-canary.14 (#12456) (`3f87769`)
- refactor: Move cache hit SHA context to verbose logging (#12435)
(`23c15b4`)
- release(turborepo): 2.8.21-canary.15 (#12457) (`6353482`)
- docs: Add missing --force flag documentation (#12440) (`e3b89b0`)
- fix: Prevent panic in turbo watch with persistent tasks (#12459)
(`337b2e8`)
- release(turborepo): 2.8.21-canary.16 (#12461) (`e79a56b`)
- fix: Support `turbo watch` in single-package workspaces (#12460)
(`ae78ce1`)
- release(turborepo): 2.8.21-canary.17 (#12463) (`0bafae2`)
- fix: Missing deps after npm lockfile parsing (#12464) (`fe5a86e`)
- release(turborepo): 2.8.21-canary.18 (#12465) (`c014134`)
- docs: Add AI agent detection and automatic markdown rewrites (#12462)
(`50bd872`)
- fix: Resolve generator name conflicts across workspaces (#12467)
(`d5d37a8`)
- release(turborepo): 2.8.21-canary.19 (#12468) (`7552e93`)
- fix: Remove root package.json from `--affected` global triggers
(#12469) (`91ebb97`)
- release(turborepo): 2.8.21-canary.20 (#12470) (`c5a4690`)
- fix: Show run summary after TUI exits (#12471) (`ffa47d1`)

---------

Co-authored-by: Turbobot <turbobot@vercel.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: site Issues and improvements related to Turborepo's documentation website

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant