Skip to content

Public sites perf · Docs: load like a static site #140

Open

Description

@s3rot0n1n

Part of the docs site performance plan (parent issue linked above). Readers expect docs to load like a static site. #71 (page weight) and #72 (caching, docs included) cover the general work; this issue covers the docs-specific gaps they leave. OpenAPI operation pages (#26, docs/openapi-support.md) inherit everything here.

Goal

Docs pages are served from cache on every view, including the first view after a publish, and the cost of a docs page doesn't grow with the size of the docs.

Tasks

  • Read only the requested page. getDocumentationSnapshot returns the full TipTap content of every published doc, and getDocumentationBySlug picks one page out of it (apps/sites/lib/api.ts:272, apps/api/src/public/routes.ts getDocumentationSnapshot). Split it into:

    • a navigation read (titles, slugs, sections, order, excerpts), used by the sidebar, feed and collection pages;
    • a per-page content read.

    Both are cached under docs:<team> (Public sites perf · Phase 2: built-in caching layer (cache components + shared Redis handler, docs cacheable) #72), and both come from one publication, so one response stays consistent (docs-management.md §23.3).

  • Warm the cache after a publish. Activation clears docs:<team>, so the first reader of each page waits for a render. After the sites docs webhook clears the tag, re-render the published docs routes (from the navigation read) in the background with bounded concurrency. The aim is static-site behaviour: the first reader after a publish gets a cached page.

  • Prefetch docs links on hover only. Sidebar, feed and collection links in apps/sites/components/themed-docs.tsx use the default Next.js Link, which prefetches every link in the viewport in production. A large sidebar fires dozens of RSC requests per page view.

  • Docs images. Check that images in the docs body (TextRenderer) go through Public sites perf · Phase 1: page weight (server-rendered blocks, lazy chat, images, fonts) #71's next/image work (width/height, lazy loading below the fold); add them if not.

  • Measure. Add a docs article, a collection page and a large docs set (300+ pages, including the OpenAPI fixture from OpenAPI support #26) to the Lighthouse sample from Public sites perf · Phase 1: page weight (server-rendered blocks, lazy chat, images, fonts) #71. Set a JavaScript budget for docs pages and check it in CI.

Constraints for future docs features

Done when

  • Docs views, including the first view after a publish, are served from cache.
  • Server render time and HTML size of a docs page don't depend on how many docs a team has.
  • Docs pages meet the targets in docs/site-delivery.md §9 (LCP < 1.5 s, CLS < 0.1, INP < 200 ms).

Activity

  1. clbotdev commented on Oct 26, 2026

    @clbotdev
    Contributor

    Data from Oct 26 (06:00 IST), measured with curl from our agents' box:

    URL TLS done TTFB HTML
    sendlit.app (FrontLit, x-nextjs-cache: HIT) 0.72 s 1.25–1.54 s 218 KB
    frontlit.app (FrontLit) 0.67 s 1.14 s 65 KB
    medialit.cloud (CourseLit) 0.66 s 1.02 s 210 KB
    www.cloudflare.com (control) 0.03 s 0.05 s —
    • About 0.65 s of each request goes to the TLS handshake with our origin (Caddy, no CDN in front), which comes down to distance and round trips. An edge cache or CDN would remove most of it.
    • Even with a Next.js cache HIT, another ~0.5 s passes between TLS completing and the first byte. That's slow for a cached page, so the HIT path is worth profiling (middleware rewrite to /sites/<teamId>, host lookup, etc.).
    • sendlit.app's HTML is 218 KB, mostly inlined RSC payload, which also slows LCP.

    Separately, SendLit BA's local Lighthouse 12.8.2 runs (median of 3) on sendlit.app went from mobile 90 to 62 and desktop 92 to 56, with LCP 6.2–6.9 s on the hero h1. Lighthouse puts the root document at 660–690 ms. Unused JS grew from 50 to 72 KiB, which ties in with #56. The render-blocking CSS is #149, and the missing preconnect to cdn.medialit.cloud is still flagged. Some of this is measurement-network noise, but the TLS and cached-TTFB numbers above are consistent across runs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions