Skip to content

Upgrade to Next.js 16.3 + migrate to App Router + adopt Cache Components - #8492

Open
aurorascharff wants to merge 44 commits into
reactjs:mainfrom
aurorascharff:upgrade-next-16
Open

Upgrade to Next.js 16.3 + migrate to App Router + adopt Cache Components#8492
aurorascharff wants to merge 44 commits into
reactjs:mainfrom
aurorascharff:upgrade-next-16

Conversation

@aurorascharff

@aurorascharff aurorascharff commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Upgrades react.dev to stable Next.js 16.3.0 and React 19.2.8, migrates from the Pages Router to the App Router, enables Cache Components, and moves MDX rendering to Server Components.

Proofread preview: https://react-92rkf6322-react-foundation.vercel.app/

Overview

  • Migrates the complete site to src/app, including section layouts, error and not-found boundaries, metadata, raw Markdown routes, llms.txt, warnings, versions, and the production error decoder.
  • Reads, compiles, and renders MDX on the server. Cached server-only helpers share content and table-of-contents data without sending the complete document through a Client Component.
  • Keeps page shells, headings, footers, not-found content, and MDX primitives server-rendered. Client boundaries are limited to interactive controls such as navigation, search, theme, copy-page, and Sandpack.
  • Uses Link from next/link for internal routes and fragments, restoring reliable client navigation and Back/Forward behavior across articles, anchors, and Sandpack pages.
  • Runs development, production builds, and bundle analysis on Turbopack. The Sandpack RSC worker is generated with esbuild instead of webpack-only loaders.
  • Fixes first-paint regressions involving the homepage logo, theme and font loading, inline-code styles, documentation layout, and Sandpack hydration.
  • Preserves titles, canonical and language alternates, Open Graph and Twitter metadata, generated OG images, development-only route behavior, and legacy React error-decoder URLs.
  • Updates ESLint 9, eslint-config-next, and eslint-plugin-react-hooks, and migrates the repository to flat ESLint configuration.

This is an architecture migration, not a redesign. The production build generates 1,042 static, SSG, or partially prerendered routes without instant = false opt-outs.

Before and final

These measurements compare the branch before the final Server/Client boundary cleanup with the current commit. Build first-load sizes are gzip; Lighthouse values are JavaScript transferred over the network and include idle route prefetches.

Route Metric Before Final Improvement
Home First-load JS 283.20 KiB 248.16 KiB -35.04 KiB (-12.4%)
Learn / Reference / Community First-load JS 286.70 KiB 221.90 KiB -64.80 KiB (-22.6%)
Blog First-load JS 244.70 KiB 210.52 KiB -34.18 KiB (-14.0%)
Home Browser-transferred JS 676.0 KB 603.5 KB -72.5 KB (-10.7%)
Effects Browser-transferred JS 742.7 KB 670.3 KB -72.3 KB (-9.7%)
useState Browser-transferred JS 742.6 KB 670.2 KB -72.4 KB (-9.7%)
  • The final cleanup removes six first-party script requests from every Lighthouse route and closes about two-thirds of the previous JavaScript gap versus main.
  • TBT remains below 40 ms and CLS remains 0 or 0.001. FCP and LCP varied significantly between Lighthouse runs, so small timing differences are not treated as conclusive.
  • HTML increased by 4.6-6.9 KB because the complete article and metadata are server-rendered. Total page transfer remained effectively flat, between -0.3% and +0.9%.
  • Lighthouse's unused-JavaScript estimate stayed within 2-4 KB of the previous preview. The cleanup reduced downloaded route/component graph code more than executed client code.
Refactoring details (AI-generated)
  • Routing and caching: moved routes to App Router conventions, added explicit section layouts and static parameters, and introduced cached server-only helpers for source files, paths, compiled MDX, metadata, and decoded errors.
  • MDX: replaced client-side deserialization with server compilation and rendering while preserving frontmatter, table-of-contents extraction, custom heading IDs, language metadata, raw Markdown output, and existing MDX components.
  • Server boundaries: moved Page, PageHeading, Footer, not-found content, and shared MDX primitives to the server. Copy-page and footer theme controls are isolated client islands.
  • Error handling: kept the required App Router error boundary client-side without importing the full page, sidebar, and MDX graph. Error and not-found pages no longer render controls that depend on unavailable Markdown endpoints.
  • Navigation: replaced Pages Router APIs with next/navigation, moved internal and fragment links to next/link, removed the dead pending-route hook, and preserved Safari scroll restoration for the iOS Back gesture.
  • Rendering: removed null Suspense holes from documentation shells, initializes theme and platform classes before paint, serves fonts from the deployment origin, and prevents headings without IDs from creating #undefined links.
  • Sandpack: passes serializable file maps into lazy client islands, keeps stable server fallback markup through hydration, prevents redundant iframe navigation from consuming browser Back, and generates the RSC worker with esbuild.
  • Turbopack: removed webpack-only imports and prefetch hints, excludes generated worker code from formatting and Tailwind scanning, and runs both development and production builds through Turbopack.
  • Metadata: restores route-derived titles, canonicals, language alternates, Algolia order, Open Graph and Twitter cards, OG fallbacks, and repeated args[] handling for legacy error-decoder URLs.
  • Analysis and tooling: adds route-level first-load JS and representative HTML to bundle reports, validates built metadata and OG assets, updates ESLint dependencies, and migrates source and local-rule tests to the flat configuration.
Verification details
  • yarn ci-check passed: Prettier, ESLint, TypeScript, heading IDs, RSS generation, and 1,859-link dead-link validation.
  • yarn test:eslint-local-rules and git diff --check passed.
  • yarn build passed with Next.js 16.3 and Turbopack, generating all 1,042 routes in 38.5 seconds.
  • The metadata audit passed for 814 concrete prerendered HTML pages, and all 218 generated OG images exist at 1200 x 630.
  • Smoke-tested Home, Blog, Learn, Reference, Community, Versions, Warnings, error decoder, scoped and generic 404s, and HTML/Markdown parity.
  • Verified direct links, reloads, fragment navigation, repeated Back/Forward traversal, and the reported Effects anchor sequence with correct URL, article content, fragment, and scroll restoration.
  • Verified browser Back around Sandpack iframes, server-rendered code highlighting and Sandpack fallbacks, production-only route filtering, and representative metadata with no console errors.
  • Tested an iPhone 13 WebKit profile. The animated edge-swipe preview still needs one physical-device check because Playwright WebKit cannot reproduce the interactive Safari gesture.

Mostly authored with agent assistance (Copilot agent mode, Claude Opus 4.7, and Codex), guided by the canary next-cache-components-adoption skill.

cc @icyJoseph

@aurorascharff
aurorascharff marked this pull request as ready for review June 22, 2026 22:49
Copilot AI review requested due to automatic review settings June 22, 2026 22:49
@aurorascharff
aurorascharff marked this pull request as draft June 22, 2026 22:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR upgrades the site to Next.js 16.3 canary + React 19.2 and migrates routing from the Pages Router to the App Router, enabling Cache Components and moving SEO/head logic to the Metadata API.

Changes:

  • Upgrade runtime/tooling (Next 16.3 canary, React 19.2, Node >=20.9, TS/ESLint adjustments) and update TS config/types.
  • Migrate content rendering to App Router routes under src/app/ with server-only helpers under src/lib/.
  • Replace the legacy <Seo>/Pages Router head setup with Metadata + root layout (src/app/layout.tsx) and add new client-side effects shims (analytics, scroll restoration).

Reviewed changes

Copilot reviewed 44 out of 51 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tsconfig.json Switch TS module resolution/JSX mode and include additional Next-generated type globs.
src/utils/compileMDX.ts Stop importing client-only MDX components by enumerating component names via a server-safe list; update return type to include languages.
src/types/jsx-bridge.d.ts Add React 19 JSX namespace bridge typings.
src/types/css.d.ts Add CSS module type shims (including @docsearch/css).
src/pages/errors/index.tsx Remove Pages Router error decoder index route.
src/pages/errors/[errorCode].tsx Remove Pages Router error decoder dynamic route + SSG logic.
src/pages/[[...markdownPath]].js Remove Pages Router catch-all MDX route + SSG path collection.
src/pages/_document.tsx Remove Pages Router custom document (head/scripts move to App Router layout).
src/pages/_app.tsx Remove Pages Router app wrapper (global CSS + effects move to App Router layout/client effects).
src/lib/readMarkdownPage.ts New server-only helper to read/compile MDX pages from src/content.
src/lib/loadErrorDecoderData.ts New server-only helper to load error code data + compile MDX for error decoder routes.
src/lib/collectPaths.ts New server-only helpers to enumerate content routes for static params generation.
src/lib/buildPageMetadata.ts New Metadata builder to replace legacy <Seo> behavior for canonical/alternates/OG/Twitter.
src/hooks/usePendingRoute.ts Remove route-transition pending-state tracking (no App Router router.events).
src/components/Search.tsx Migrate navigation to next/navigation and make Search a client component.
src/components/PageHeading.tsx Migrate router usage to usePathname.
src/components/MDX/MDXComponentsList.ts New server-safe list of MDX component names for MDX compilation.
src/components/MDX/MDXComponents.tsx Mark as client component; document syncing with MDXComponentsList.
src/components/MDX/ExpandableExample.tsx Replace next/router hash parsing with window.location.hash on client.
src/components/MDX/Challenges/Challenges.tsx Replace next/router usage with App Router primitives + hash handling.
src/components/Layout/useDeserializedMDX.tsx New client hook to deserialize MDX JSON into React nodes.
src/components/Layout/TopNav/TopNav.tsx Migrate routing to usePathname.
src/components/Layout/Sidebar/SidebarRouteTree.tsx Migrate routing to usePathname.
src/components/Layout/Page.tsx Convert to client component; remove <Seo>/next/head; accept pathname from server; render content with new MDX deserialization flow.
src/components/Layout/HomeContent.js Switch from custom use polyfill to React’s built-in use.
src/components/ErrorDecoderContext.tsx Update comments to reflect App Router/server component usage.
src/components/Seo.tsx Remove legacy SEO component in favor of Metadata API.
src/app/warnings/[slug]/page.tsx New App Router route for warnings pages (static params + metadata).
src/app/versions/page.tsx New App Router route for versions page (cache + metadata).
src/app/renderSectionPage.tsx New shared server renderer + metadata helper for sectioned MDX pages.
src/app/reference/[[...slug]]/page.tsx New App Router catch-all route for reference section.
src/app/page.tsx New App Router home route using server MDX read + metadata builder.
src/app/not-found.tsx New App Router not-found page using <Page> with explicit pathname/section.
src/app/llms.txt/route.ts Migrate llms.txt generator to App Router route handler (GET).
src/app/learn/[[...slug]]/page.tsx New App Router catch-all route for learn section.
src/app/layout.tsx New root layout: global CSS, viewport/metadata base, scripts, preloads, and client effects.
src/app/errors/page.tsx New App Router index route for error decoder.
src/app/errors/ErrorDecoderView.tsx New client view to deserialize/render error decoder MDX and provide context.
src/app/errors/[errorCode]/page.tsx New App Router dynamic error decoder route + static params + metadata.
src/app/error.tsx New global error boundary page for App Router.
src/app/DocsPage.tsx New client wrapper to feed deserialized MDX + toc into <Page>.
src/app/community/[[...slug]]/page.tsx New App Router catch-all route for community section.
src/app/clientEffects.tsx New client-only analytics + scroll restoration effects (replacing _app.tsx).
src/app/blog/[[...slug]]/page.tsx New App Router catch-all route for blog section.
src/app/api/md/[...path]/route.ts Migrate legacy API route to App Router route handler (GET).
package.json Update scripts for --webpack, adjust lint scripts, bump Next/React/types, and raise Node engine.
next.config.js Enable Cache Components + React compiler settings; add serverExternalPackages; keep webpack-based builds.
next-env.d.ts Update Next TypeScript env declarations (now includes .next type imports).
CLAUDE.md Update repo structure notes and add Next agent rules block.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/app/layout.tsx
Comment thread src/app/layout.tsx
Comment thread src/lib/readMarkdownPage.ts Outdated
Comment thread package.json Outdated
Comment thread src/components/Layout/Page.tsx
aurorascharff added a commit to aurorascharff/react.dev that referenced this pull request Jun 22, 2026
From Copilot's automated review on PR reactjs#8492:

- layout.tsx: render `<meta property="fb:app_id">` as a property tag
  directly in <head> (Next's `metadata.other` only emits name=).
- layout.tsx: restore the RSS autodiscovery <link> and the Algolia
  preconnect <link> that lived in the old Pages Router <Head>.
- readMarkdownPage.ts: switch readFileSync -> fs.promises.readFile to
  avoid blocking the event loop while compiling MDX.
- package.json: bump @types/node from ^14 to ^20 to match the new
  engines.node >=20.9.0.
- buildPageMetadata.ts + renderSectionPage.tsx + learn/blog
  generateMetadata: thread the section's routeTree through so we can
  re-emit the `algolia-search-order` meta tag on Learn pages and Blog
  posts (matches the old <Seo> behavior; Algolia uses it for ordering).
Runs `npx @next/codemod@canary upgrade canary`:

- next 15.1.12 → 16.3.0-canary.60
- react / react-dom ^19.0.0 → 19.2.7 (pinned)
- @types/react / @types/react-dom 19.2.x (pinned via resolutions)
- eslint-config-next 12.0.3 → 14 (last version supporting ESLint 7;
  16.x requires ESLint 8+ which is a separate upgrade)
- engines.node >=16.8.0 → >=20.9.0 (Next 16 minimum)

Type shims for React 19's stricter @types/react:

- src/types/jsx-bridge.d.ts re-exports React.JSX as the global JSX
  namespace used by existing Icon components
- src/types/css.d.ts provides ambient declarations for .css side-effect
  imports (needed under moduleResolution: 'bundler')
- @types/prop-types added (transitively required by legacy
  forwardRefWithAs.tsx)

Auto-updates from the codemod:

- tsconfig.json: moduleResolution 'node' → 'bundler', jsx 'preserve' →
  'react-jsx', .next/dev/types/** added to include
- next-env.d.ts: routes.d.ts and root-params.d.ts references
- CLAUDE.md: nextjs-agent-rules block injected by next dev (intended
  to be committed per the block's own instructions)
Migrates the docs site from the Pages Router to the App Router and
enables Cache Components (`experimental.cacheComponents` now top-level
`cacheComponents: true`).

Route tree (`src/app/`):

- `layout.tsx` — root layout (theme/uwu init script, fonts, GA, scroll
  restoration); replaces _document.tsx + _app.tsx
- `page.tsx` — home (`section: 'home'`)
- `{learn,reference,community,blog}/[[...slug]]/page.tsx` — section-
  specific catch-all docs pages (each owns its sidebar tree, section
  literal, and metadata generator)
- `warnings/[slug]/page.tsx`, `versions/page.tsx` — flat sections
- `errors/page.tsx`, `errors/[errorCode]/page.tsx` — error decoder
- `not-found.tsx`, `error.tsx` — error/404 boundaries
- `api/md/[...path]/route.ts`, `llms.txt/route.ts` — Route Handlers
  replacing the old API/static endpoints
- `renderSectionPage.tsx`, `DocsPage.tsx`, `clientEffects.tsx` —
  shared route helpers (server + client respectively)

Shared helpers (`src/lib/`, server-only):

- `readMarkdownPage.ts` — MDX read + compile from src/content
- `collectPaths.ts` — generateStaticParams enumeration
- `buildPageMetadata.ts` — canonical URL + hreflang alternates +
  open graph (replaces the old <Seo> component)
- `loadErrorDecoderData.ts` — error-codes fetch + MDX compile

Component-level changes:

- `next/router` → `next/navigation` across Page.tsx, Search.tsx,
  PageHeading.tsx, TopNav.tsx, SidebarRouteTree.tsx,
  ExpandableExample.tsx, Challenges.tsx
- `Page.tsx` takes `section` and `pathname` as props instead of
  sniffing them at runtime via `usePathname`; `<Seo>` and `<Head>`
  removed (replaced by App Router Metadata API)
- `MDXComponents.tsx` is now `'use client'` so the registry can host
  client-only components (Sandpack etc.); a parallel server-safe
  `MDXComponentsList.ts` keeps the component name list reachable from
  `compileMDX.ts`
- `useDeserializedMDX.tsx` — shared hook for revival of the
  serialized React tree on the client
- `usePendingRoute.ts` is a no-op (App Router has no equivalent of
  `router.events.routeChangeStart`; <Link> handles transitions)

next.config.js:

- `cacheComponents: true` and `reactCompiler: true` lifted out of
  `experimental`
- `serverExternalPackages` lists the Babel + MDX deps that compileMDX
  loads via runtime `require` (otherwise Next bundles them and breaks
  inside the cache scope)
- `turbopack: {}` placeholder so the build picks the webpack pipeline
  (the project's existing webpack config + Sandpack `raw-loader`
  imports aren't Turbopack-compatible yet)

Cache Components adoption:

- `'use cache'` on every page default export and `generateMetadata`
  (including `/errors/[errorCode]`)
- Zero `export const instant = false` opt-outs
- Build output: 816 routes, all Static or Partial Prerender, only
  `/api/md/[...path]` is dynamic

Other:

- `use(promise)` polyfill in HomeContent.js dropped in favour of
  React 19's built-in `use`
- `ErrorDecoderContext.tsx` doc comment refreshed to reflect that
  context now flows from a server component, not getStaticProps
- `worker-bundle.dist.js` regenerated by scripts/buildRscWorker.mjs
  with the updated React 19.2 runtime (much smaller)
From Copilot's automated review on PR reactjs#8492:

- layout.tsx: render `<meta property="fb:app_id">` as a property tag
  directly in <head> (Next's `metadata.other` only emits name=).
- layout.tsx: restore the RSS autodiscovery <link> and the Algolia
  preconnect <link> that lived in the old Pages Router <Head>.
- readMarkdownPage.ts: switch readFileSync -> fs.promises.readFile to
  avoid blocking the event loop while compiling MDX.
- package.json: bump @types/node from ^14 to ^20 to match the new
  engines.node >=20.9.0.
- buildPageMetadata.ts + renderSectionPage.tsx + learn/blog
  generateMetadata: thread the section's routeTree through so we can
  re-emit the `algolia-search-order` meta tag on Learn pages and Blog
  posts (matches the old <Seo> behavior; Algolia uses it for ordering).
The App Router migration and Cache Components (`cacheComponents`,
`'use cache'`) are all supported in stable 16.2.9 — the canary pin was
only needed during the migration itself. Builds clean with no config
warnings; all 816 routes still Static / Partial Prerender.

We removed every `instant = false` opt-out, so the 16.3-only `instant`
route config isn't used and there's nothing tying us to canary.
Per review feedback from @icyJoseph: instead of 'use cache' on every
page and generateMetadata, cache at the utility/data layer so callers
just work and the page render + generateMetadata share one compile.

- readMarkdownPage, collectSectionPaths, collectFlatSectionSlugs,
  loadErrorCodes, and a new compileErrorDecoderData helper are now
  'use cache' + cacheLife('max') (stable content, only changes on
  deploy; revalidate 30d instead of the default 15m).
- loadErrorDecoderData stays uncached and keeps the notFound() check,
  which can't run inside a 'use cache' scope; it delegates the cached
  compile to compileErrorDecoderData.
- Removed 'use cache' from all 9 routes and their generateMetadata.

Build unchanged: 816 routes, all Static / Partial Prerender.
@MaxwellCohen

Copy link
Copy Markdown
Contributor

@aurorascharff, I am so glad to see you talking up the challenge of moving to app router. I really hope that we can get this upgrade over the finish line.

I did some quick performance testing on the useActionState page (this pr build on my machine vs react.dev), and INP and LCP are the same, which is good. When I tested the previous attempt to use app router #8338 there was a slight decrease.

I am, however, still seeing an increase in JS sent to the browser (2.2 MB vs 2.0 MB) and used (1.1 MB vs 971 KB).

localhost JS coverage report from Chrome
image

Production JS coverage report from Chrome
image

Webpack build analysis shows a slight increase in client size bundles (920.36 KB gzip vs 880.62 KB gzip)

This PR
image

Main
image

On your maybe items

'use client' boundary on MDXComponents.tsx — there might be a cleaner split.

IMO MDXComponents.tsx should essentially be a barrel file for React.lazy, Next.js's dynamic components, or server components. This can greatly reduce the JavaScript bundle size. I created a proof-of-concept PR #8373 that can serve as a reference.

Per-section catch-alls vs one big catch-all — current shape avoids runtime section sniffing but means N near-identical page.tsx files.

The src/components/Layout/Page.tsx is really a mix-and-match of the diffrent layouts, so splitting that into the diffrent layouts.tsx and having the app router's page.tsx calls MDX render as a server component might be a good approach. I personally do not like catch-all routes because they result in JS-heavy pages and extra logic that I think should be handled by Next.js. Resulting in problems like currently, every page has all the home page info in the JS bundle

Overall, these changes make the site feel faster to navigate.

Per review feedback from @MaxwellCohen: <Page> is a shared client
component rendered by every route, and it statically imported
HomeContent (~2.7k LOC of homepage-only marketing/animation code).
That pulled the homepage into the shared client bundle of every docs,
reference and blog page.

Load it with next/dynamic instead. The homepage chunk (~116 KB) now
loads only at '/'; a representative docs route drops from ~941 KB to
~830 KB of client JS.
Per review feedback from @icyJoseph: the /api/md/[...path] handler that
serves raw markdown was Dynamic (a function per request). The content
set is fixed at build time, so prerender it instead.

- Add generateStaticParams (new collectAllContentPaths in collectPaths)
  enumerating every .md under src/content.
- 'use cache' + cacheLife('max') the file read (the dynamic/dynamicParams
  route segment configs are disallowed under cacheComponents).

Result: /api/md/[...path] is now SSG; +221 prerendered markdown
endpoints. Served from the CDN, so self-hosted clones stay static too.
Paths that match a section catch-all but have no backing .md file (e.g.
/learn/state, a sidebar header) were 500ing. The fs read threw inside
readMarkdownPage's 'use cache' scope, which surfaces as a render error
instead of falling through to notFound().

readMarkdownPage now returns PageData | null for a missing file instead
of throwing; callers decide notFound(). Removes the now-redundant
safeReadPage try/catch wrapper.
Expand the comment above the empty `turbopack: {}` to explain why the
build runs on webpack (custom webpack config + Sandpack raw-loader
imports aren't Turbopack-ready) and that it's a tracked follow-up.
@aurorascharff
aurorascharff marked this pull request as ready for review June 23, 2026 10:45
@aurorascharff

aurorascharff commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator Author

@aurorascharff, I am so glad to see you talking up the challenge of moving to app router. I really hope that we can get this upgrade over the finish line.

I did some quick performance testing on the useActionState page (this pr build on my machine vs react.dev), and INP and LCP are the same, which is good. When I tested the previous attempt to use app router #8338 there was a slight decrease.

I am, however, still seeing an increase in JS sent to the browser (2.2 MB vs 2.0 MB) and used (1.1 MB vs 971 KB).

localhost JS coverage report from Chrome image

Production JS coverage report from Chrome image

Webpack build analysis shows a slight increase in client size bundles (920.36 KB gzip vs 880.62 KB gzip)

This PR image

Main image

On your maybe items

'use client' boundary on MDXComponents.tsx — there might be a cleaner split.

IMO MDXComponents.tsx should essentially be a barrel file for React.lazy, Next.js's dynamic components, or server components. This can greatly reduce the JavaScript bundle size. I created a proof-of-concept PR #8373 that can serve as a reference.

Per-section catch-alls vs one big catch-all — current shape avoids runtime section sniffing but means N near-identical page.tsx files.

The src/components/Layout/Page.tsx is really a mix-and-match of the diffrent layouts, so splitting that into the diffrent layouts.tsx and having the app router's page.tsx calls MDX render as a server component might be a good approach. I personally do not like catch-all routes because they result in JS-heavy pages and extra logic that I think should be handled by Next.js. Resulting in problems like currently, every page has all the home page info in the JS bundle

Overall, these changes make the site feel faster to navigate.

Thanks Maxwell! The +40 KB was from before I pushed a couple of follow-ups. I re-measured the useActionState page on both branches locally and it's basically at parity now, around 265 KB gzip on main vs 263 KB here. The fix was exactly what you spotted: the Page component statically imported HomeContent, so every page was shipping the homepage. It's dynamically imported now, so only the home route loads it.

On the architecture ideas, both are in the follow-ups section and I agree with the direction. I prototyped the MDXComponents barrel like in your POC: it works, but only saved about 7 KB, since the heavy part (CodeMirror) is already lazy-loaded inside SandpackRoot. The Page-to-layouts split is the bigger win and would also let MDX deserialize on the server, but it's a larger change, so I'd rather land the migration first and do that as a dedicated follow-up. What do you think?

@MaxwellCohen

Copy link
Copy Markdown
Contributor

Thank you for the performance updates. The lazy loading helps get JS downloaded to the same level as prod, great work!

image

@MaxwellCohen

Copy link
Copy Markdown
Contributor

Thanks Maxwell! The +40 KB was from before I pushed a couple of follow-ups. I re-measured the useActionState page on both branches locally and it's basically at parity now, around 265 KB gzip on main vs 263 KB here. The fix was exactly what you spotted: the Page component statically imported HomeContent, so every page was shipping the homepage. It's dynamically imported now, so only the home route loads it.

On the architecture ideas, both are in the follow-ups section and I agree with the direction. I prototyped the MDXComponents barrel like in your POC: it works, but only saved about 7 KB, since the heavy part (CodeMirror) is already lazy-loaded inside SandpackRoot. The Page-to-layouts split is the bigger win and would also let MDX deserialize on the server, but it's a larger change, so I'd rather land the migration first and do that as a dedicated follow-up. What do you think?

Very reasonable plan, at the end of the day, I want to see react.dev to be as fast as possible and showcase React's greatness both in content and implementation. There will be many changes because React and Next.js have evolved significantly over the last 3 years.

* Resize long og image text

* address feedback

(cherry picked from commit 7b6c3ce)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants