Skip to content

Per-symbol API data: shrink /docs SSR payload and restore API-page head()/notFound() #11

Description

@dogmar

Follow-up to #9 (merged as cdafc34). Two related regressions in the API-reference docs were deferred from that PR's review because the clean fix is a single design change rather than a patch.

Problem

1. The /docs layout loader ships the full API symbols JSON in every page's SSR payload.
website/src/routes/docs.tsx returns apiData (the whole ~250KB api-data/symbols.gen.json) from its loader. The dynamic import() keeps it out of the client JS bundle, but returning it from the loader serializes it into the dehydration payload of every /docs/* page — including pure-content pages like /docs/getting-started that render no API tables. This defeats the lazy-loading goal: pre-#9, the layout shipped only {name, kind} nav entries and symbol pages fetched a single symbol server-side.

2. API symbol pages lost head() and notFound() semantics.
website/src/routes/docs/api/$symbol.tsx:

  • No head() — every API page inherits the generic site title and has no per-symbol <title>/meta description (SEO + tab-title regression vs. main).
  • The loader never throws notFound(), so /docs/api/DoesNotExist SSRs with HTTP 200, the declared notFoundComponent is dead code, and it's byte-duplicated by an inline "Symbol not found" fallback in the component body.

Recommended approach (option C from the #9 discussion)

Split the build-time output into per-symbol chunks instead of one monolith:

  • website/scripts/extract-api.ts emits api-data/<symbol>.gen.json per symbol, plus a small api-data/index.gen.json (name → kind) for nav + link detection.
  • docs.tsx loader returns only the small index (drop apiData from loader data).
  • docs/api/$symbol.tsx loader imports the single <symbol>.gen.json, throws notFound() when missing (server fn or loader), and sets head() title/description from it. Delete the inline fallback — let notFoundComponent handle it.
  • TypeLink tooltips dynamic-import the hovered symbol's chunk on demand instead of reading a page-wide apiData.
  • Broaden the ambient declaration added in docs: precompile Markdoc into React route components at build time #9 from */api-data/symbols.gen.json to */api-data/*.gen.json so all chunks resolve without being checked in (they stay gitignored + generated, same as today).

Also in scope (same area, smaller)

  • Restore a branded docs 404. Deleting the dynamic $slug.tsx in docs: precompile Markdoc into React route components at build time #9 removed the docs-layout notFoundComponent, so unknown /docs/<slug> URLs now hit TanStack Router's bare default not-found instead of the styled page. Add a notFoundComponent to docs.tsx (or a root defaultNotFoundComponent).

Verification

  • Confirm /docs/getting-started SSR HTML no longer contains the symbols payload (was ~250KB).
  • /docs/api/<Known> has the correct <title> and meta description; /docs/api/<Bogus> returns 404 and renders the branded not-found.
  • API tables still SSR (don't regress to client-only rendering).
  • vp run ready passes with the file absent (ambient declaration covers the per-symbol chunks).

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions