Skip to content

seo: serve the docs at webjs.dev/docs with the marketing site chrome #1098

Description

@vivek7405

Problem

The documentation lives at docs.webjs.dev, a separate subdomain. Two costs.

SEO. Google treats a subdomain as a semi-separate entity, so the docs (the largest
body of indexable content the project has, 45 routes) build authority for
docs.webjs.dev instead of for webjs.dev. A path on a strong domain beats a subdomain
of it. This is the same principle that led to dropping a planned GitHub Pages site in
#1088: consolidate signals on one domain rather than splitting them.

The subdomain also currently serves no robots.txt and no sitemap.xml (both 404,
verified), so it is likely thinly indexed today. That is the good news: there is little
accumulated equity to disturb, which makes NOW the cheapest this migration will ever be.

Design drift. The docs site has its own layout, nav, and footer, diverging from the
marketing site. A reader crossing from webjs.dev to the docs leaves one design system
and enters another.

Design / approach

Serve the docs at webjs.dev/docs, sharing the marketing site's chrome.

Simplify the docs design to fit the existing webjs.dev shell. The docs should use
the SAME top navbar and footer as /what-is-webjs, /why-webjs, /compare, and /blog,
not a parallel set. The only docs-specific chrome is the sidebar nav for the page tree.
Everything else (header, footer, type scale, tokens, code-block styling) comes from the
marketing site so the docs read as part of one site rather than a bolted-on subdomain.

Two viable implementations, pick during the work:

  1. Merge into the website/ app as website/app/docs/**. Cleanest result (literally
    one app, one chrome, one sitemap, no proxy) and the shared layout comes free. Cost is a
    real refactor of 45 routes plus the docs content pipeline.
  2. Keep docs/ a separate service and set webjs.basePath = '/docs' (Support basePath/assetPrefix for sub-path deployments #256, built for
    exactly this), with a routing layer in front of Railway sending /docs/* to that
    service. Less code motion, but the chrome must then be duplicated or extracted into a
    shared package, which risks exactly the drift this issue is meant to end.

Recommendation: option 1. The stated goal is design consistency, and option 2 keeps
two layouts alive. Option 1 also collapses the sitemap and robots story into the one that
already works on webjs.dev.

docs.webjs.dev must keep resolving forever, as a redirect-only host. Path-preserving
301s (docs.webjs.dev/docs/x to webjs.dev/docs/x). Framework error messages in SHIPPED
npm packages point at docs.webjs.dev (see below), so old installed versions will keep
sending users there indefinitely.

Implementation notes (for the implementing agent)

Where to edit:

  • Source app: docs/ (its own WebJs app, 45 page.ts routes under docs/app/docs/**,
    plus docs/app/docs/layout.ts which holds the sidebar). Content is mostly in-page
    TypeScript, with only 4 .md files, so the migration is route-by-route rather than a
    content pipeline port.
  • Target: website/app/docs/** if taking option 1.
  • Shared chrome: website/app/layout.ts (the NAV array and the root shell) and
    website/lib/site-footer.ts. The docs must reuse these, not copy them. Note the footer
    is rendered from the ROOT LAYOUT on every page (feat(website): render site footer from the root layout on every page #1082), so a merged docs route gets it
    automatically.
  • Design tokens: website/public/input.css (the @theme block). The docs app has its
    own copy that must be dropped, not merged, or the two drift again.
  • Sitemap: website/app/sitemap.ts, add the docs routes. It already reads live content
    queries, so follow that pattern rather than hardcoding 45 paths.
  • Redirects: docs.webjs.dev needs path-preserving 301s. Either a Cloudflare rule or a
    tiny redirect-only service on Railway.
  • Hardcoded URLs in SHIPPED packages (update for future releases, but they can never be
    retroactively fixed in already-published versions):
    • packages/core/src/component.js L579
    • packages/server/src/actions.js L270
    • packages/cli/lib/create.js L1295, L1366, L1500
    • packages/cli/bin/webjs.js L878
  • Docs surfaces referencing the subdomain: AGENTS.md, README.md,
    .agents/skills/webjs/**, packages/cli/templates/**, and website/lib/links.ts
    (DOCS_URL).

Landmines / gotchas:

  • Do NOT add sitemap.ts / robots.ts to docs.webjs.dev first. Getting the
    subdomain properly indexed immediately before moving it maximizes migration cost. Migrate,
    then let the webjs.dev sitemap cover the new paths.
  • Sequence this BEFORE any Search Console reindex request, or a reindex cycle is spent
    on URLs that are about to move.
  • website/lib/links.ts reads DOCS_URL from env with a production fallback. Every
    in-repo app and the Railway env vars set DOCS_URL, so changing the shape means touching
    the Railway service variables too (DOCS_URL is set on the website service).
  • The docs app has its own llms.txt AND llms-full.txt routes
    (docs/app/llms.txt/route.ts, docs/app/llms-full.txt/route.ts). The website has its own
    llms.txt. Decide the merged story deliberately rather than ending up with two.
  • The prose hook (.claude/hooks/block-prose-punctuation.sh) blocks em-dashes, space-hyphen
    and space-semicolon pauses, and enforces WebJs capitalization. It also fires on shell ;
    separators in Bash tool calls.
  • Invariant 8: only the ROOT layout may write <!doctype> / <html> / <head> / <body>.
    A merged docs layout is a NON-root layout and must not.
  • Invariant 9: no backticks inside html template bodies. The docs are full of code
    samples, so route every one through the existing highlight helper on a plain string.
  • webjs.dev is behind Cloudflare. A ?v-less tailwind.css goes stale at the edge across
    a deploy (fix: fingerprint author-written asset links so a deploy busts the CDN copy #1095), so verify the merged docs against a locally rebuilt stylesheet, never
    against production.

Invariants to respect: AGENTS.md invariants 8, 9, 11.

Tests + docs surfaces:

  • SSR tests for the migrated routes under website/test/ssr/, mirroring the existing
    page-level tests.
  • A test asserting the docs routes render the SHARED nav and footer (the whole point of the
    redesign), which fails if someone reintroduces a docs-specific shell.
  • A test asserting the sitemap includes docs routes.
  • A counterfactual proving each new assertion fires.
  • Update AGENTS.md, README.md, the skill references, and the scaffold templates for the
    new URL.

Acceptance criteria

  • Documentation is served at webjs.dev/docs/**
  • Docs pages render the SAME top navbar and footer as /what-is-webjs and /why-webjs
  • Docs pages use the marketing site's design tokens, with no duplicate @theme block
  • The only docs-specific chrome is the page-tree sidebar
  • docs.webjs.dev/** path-preservingly 301s to webjs.dev/docs/**
  • docs.webjs.dev remains resolvable indefinitely (shipped packages link to it)
  • Docs routes appear in webjs.dev/sitemap.xml
  • Hardcoded docs URLs in packages/** updated for future releases
  • AGENTS.md, README.md, skill references, and scaffold templates updated
  • A counterfactual proves the shared-chrome test actually fires
  • Verified against a locally rebuilt stylesheet, not production

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions