Test iOS Safari header flicker fixes - #8621
Closed
aurorascharff wants to merge 62 commits into
Closed
Conversation
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.
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.
These files dropped the '/* Copyright (c) Facebook, Inc. */' banner when the 'use client' directive was added. Restore it above the directive (comments may precede a directive without disabling it).
Reading window.location.hash during render returned false under the App Router (the server render sees no hash), so the queued-expand ref stuck at false and deep-linked DeepDive/Example blocks no longer opened on load. Move the hash check into the post-mount effect so the initial render stays deterministic and the deep link expands again after hydration.
# Conflicts: # package.json # src/components/Layout/Page.tsx # src/pages/errors/[errorCode].tsx # yarn.lock
…into upgrade-next-16
… components for community, learn, and reference sections
* Resize long og image text * address feedback (cherry picked from commit 7b6c3ce)
The Pages Router build always hid the generated /src/styles.css tab (the old check read a `visible` flag that was never set). Restoring that so ~76 pages don't grow a second tab. Co-Authored-By: Claude <noreply@anthropic.com>
The font URLs were made relative in this PR, but translation forks rely on loading (and sharing the cache for) the Optimistic fonts from react.dev. Restores the absolute URLs in the CSS and the preloads. Co-Authored-By: Claude <noreply@anthropic.com>
`send_page_view: false` suppressed GA4's automatic page_view on landing,
and the custom `pageview` event was sent for every pathname including the
initial one. Match the old _app.tsx: let gtag('config') report the landing
page_view, and only send the legacy custom event on client navigations.
Co-Authored-By: Claude <noreply@anthropic.com>
- Only collect top-level headings (run the extractor before the MaxWidth wrapper). Headings nested in <Note> etc. were never in the TOC before. - Compile TOC entries from the already-processed mdast heading children instead of re-parsing the heading source. Re-parsing dropped smartypants (straight quotes on 43 pages) and turned headings like "1. Install" into an <ol> inside the TOC link. Co-Authored-By: Claude <noreply@anthropic.com>
Reading src/content with `fs` inside 'use cache' meant that in development an edited .md file kept serving stale content on a normal reload (only a hard refresh bypassed the cache), and nothing triggered a refresh on save now that next-remote-watch is gone. Import the files via import.meta.glob with Turbopack's built-in raw-loader instead. The content is part of the module graph, so saving a .md file Fast Refreshes the open page in place, and production bundles the files rather than depending on outputFileTracingIncludes. Co-Authored-By: Claude <noreply@anthropic.com>
- Error decoder: key the compiled-MDX cache on the error message instead of the entire codes.json map. - Home page clock: tick on minute boundaries again instead of every 60s from mount. - next.config: drop the @babel/* entries from serverExternalPackages; the server no longer uses Babel. Co-Authored-By: Claude <noreply@anthropic.com>
Not-found pages under the dynamic section routes are rendered entirely on the client (the PPR fallback shell is empty), and React doesn't execute inline scripts it inserts. So the theme/uwu/platform init never ran there: the page came up light regardless of preference, and the theme toggle threw because window.__setPreferredTheme didn't exist. Move the script to a shared module and evaluate it from a client effect when the inline version hasn't run. Co-Authored-By: Claude <noreply@anthropic.com>
The Pages Router's Link happened to resolve [Setup](learn/setup) against the site root. The App Router's Link resolves it against the current page (/learn/learn/setup), so spell the paths out. Co-Authored-By: Claude <noreply@anthropic.com>
useTocHighlight built its own list of headings by scanning the DOM for
anchor elements inside H1/H2/H3, and assumed that list lined up with the
TOC. It only did so because the page title rendered a broken #undefined
anchor that happened to pad index 0 ("Overview"); with that gone, the
previous entry got highlighted. Headings nested in <Note> etc. have
anchors but aren't in the TOC, which skewed it on those pages too.
Resolve the TOC entries' own ids to elements instead, so there is one
source of truth.
Co-Authored-By: Claude <noreply@anthropic.com>
The menu state became "open for this path", derived against the current pathname so the overlay disappears as soon as a navigation commits. But the path was never forgotten, so navigating Back to the page the menu was opened on brought the menu back with it. Clear it once the path changes. Co-Authored-By: Claude <noreply@anthropic.com>
Size changesDetailsNo comparable base-branch data yet — the base branch has not produced stats in this format. Showing current sizes only; deltas will appear on the next run after this lands on the base branch.
|
This reverts commit 92bdeff.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
This draft copies the complete Next.js 16 upgrade from #8492 and provides an isolated branch for deploying and testing iOS Safari Back and Forward header flicker experiments.
Results so far
Current experiment
Cache Components remains enabled, but a temporary build patch reduces Next.js route-tree retention from three entries to one. This prevents inactive routes from being kept hidden and restored through React Activity while preserving the active route and Cache Components behavior.
This preview isolates the Activity/BF-cache behavior by itself. If the flicker disappears, inactive Activity route restoration is involved.
This is a temporary diagnostic PR and is not intended to merge as-is.