Repository navigation
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The provider documentation incorrectly claims every server-loaded block skips the lazy enhancer despite supported override and opt-out paths.
Review effort: Balanced
Findings: 1
What changed in this PR
Prevents lazy emphasis enhancement from causing server/client hydration mismatches.
Changes:
- Applies default emphasis during server-side code loading.
- Defers lazy client enhancers until hydration completes.
- Adds unit, hydration, E2E, and documentation coverage.
| File | Description |
|---|---|
AGENTS.md |
Documents the hydration issue and reproduction path. |
packages/docs/src/withFairGardenDocs/withFairGardenDocs.ts |
Exposes emphasis options to server loaders. |
packages/docs/src/withFairGardenDocs/withFairGardenDocs.test.ts |
Tests environment configuration. |
packages/docs/src/useCode/useSourceEnhancing.ts |
Defers lazy enhancers through hydration. |
packages/docs/src/useCode/useSourceEnhancing.test.ts |
Tests applied-enhancer skipping. |
packages/docs/src/useCode/useSourceEnhancing.hydration.test.tsx |
Covers lazy-enhancer hydration behavior. |
packages/docs/src/pipeline/enhanceCodeEmphasis/enhanceCodeEmphasisLazy.ts |
Marks the enhancer as lazy. |
packages/docs/src/pipeline/enhanceCodeEmphasis/enhanceCodeEmphasisLazy.test.ts |
Tests the lazy marker. |
packages/docs/src/CodeHighlighter/types.ts |
Documents new enhancer and server defaults. |
packages/docs/src/CodeHighlighter/createDefaultSourceEnhancers.ts |
Creates configured default server enhancers. |
packages/docs/src/CodeHighlighter/createDefaultSourceEnhancers.test.ts |
Tests default enhancer creation. |
packages/docs/src/CodeHighlighter/CodeSourceLoader.tsx |
Enhances all server-loaded files. |
packages/docs/src/CodeHighlighter/CodeSourceLoader.test.tsx |
Tests server enhancement and overrides. |
packages/docs/src/CodeHighlighter/CodeInitialSourceLoader.tsx |
Enhances initial server-loaded sources. |
packages/docs/src/CodeHighlighter/CodeInitialSourceLoader.test.tsx |
Tests initial-source enhancement. |
packages/docs/src/CodeHighlighter/CodeHighlighter.hydration.test.tsx |
Tests the complete hydration path. |
docs/app/lib/pipeline/with-fairgarden-docs/types.md |
Updates generated option documentation. |
docs/app/lib/pipeline/with-fairgarden-docs/page.mdx |
Explains shared emphasis configuration. |
docs/app/lib/hooks/use-code/page.mdx |
Documents server, client, and lazy enhancers. |
docs/app/lib/hooks/page.mdx |
Adds lazy enhancers to navigation. |
docs/app/lib/components/code-provider/page.mdx |
Updates lazy-provider behavior guidance. |
docs/app/lib/components/code-highlighter/types.md |
Updates generated prop documentation. |
docs/app/lib/components/code-highlighter/demos/code-inline-hydration/test.ts |
Adds hydration and SSR E2E coverage. |
docs/app/lib/components/code-highlighter/demos/code-inline-hydration/page.tsx |
Adds the regression fixture route. |
docs/app/lib/components/code-highlighter/demos/code-inline-hydration/InlineCodeContent.tsx |
Simulates delayed hydration content. |
docs/app/lib/components/code-highlighter/demos/code-inline-hydration/InlineCode.tsx |
Defines inline-code regression cases. |
docs/app/lib/components/code-highlighter/demos/code-inline-hydration/CodeNote.tsx |
Provides lazily loaded fixture content. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+253
to
+257
| `CodeProviderLazy` it is a lazy wrapper that a precomputed or server-loaded block | ||
| skips without loading (the build or the server already ran it), and that an | ||
| editing/highlighting block warms before it is needed. It never runs while the | ||
| page hydrates, so whether it has loaded yet can't make the hydrated markup differ | ||
| from the server HTML; a block that still needs it gets it right after hydration. |
This branch was successfully deployed
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.

Summary
Under
CodeProviderLazy, inline code with the defaulthighlightAftercould log a hydration mismatch on the first request afternext devstarted: a frame rendered withoutdata-frame-typeon the server and with"focus"on the client, or the reverse. React doesn't patch mismatched attributes, so the wrong frames also stayed on screen.Cause
useCoderan the provider'senhanceCodeEmphasisLazyduring the server render and the hydration. That enhancer returns a result synchronously only once its chunk has loaded in the current process, and a promise before. The server and the browser load it independently, so the hydrated tree depended on which side had loaded it. The same could happen with any enhancer that returns sync-or-promise, and in theContentLoadingfallback thatbuildStringFallbackprepares.Fix
useSourceEnhancingandbuildStringFallbackrun only enhancers marked with the new optionalenhancerSyncflag. The built-increateEnhanceCodeEmphasisenhancers set it, so their frames stay in the server HTML. Every other enhancer the tree hasn't recorded runs right after hydration, so no enhancer can cause a mismatch, flagged or not.sourceEnhancerstoCodeHighlighter. The server records the enhancer, and the provider's enhancer of the same name skips the tree without loading.createLazySourceEnhancer(enhancerName, load)(new, at@fairgarden/docs/pipeline/createLazySourceEnhancer): builds an enhancer that loads on demand. It loads once and caches the result, returns synchronously once loaded, haspreload(), and requires the loaded enhancer's name so upstream-enhanced trees are never enhanced twice.enhanceCodeEmphasisLazyis now built with it.CodeHighlighterClienttracks hydration only for the'hydration'modes.Behavior change: when custom enhancers run
The new
enhancerSyncflag declares that an enhancer never returns a promise and produces the same output on the server and in the browser for the same input. That means no browser-only state, timing or lazily loaded code; options or state that are the same on both sides are fine. What changes depends on where the enhancer is passed.useCode, aCodeProvider/CodeProviderLazyor aCodeControllerContext:enhancerSync: trueruns right after hydration. Its output isn't in the server HTML, and appears on the first render after hydration.enhancerSync: true, it runs during the server render and hydration as before.CodeHighlighter'ssourceEnhancers:ContentLoadingfallback window. Now onlyenhancerSyncenhancers are applied to the fallback, so an unmarked one doesn't window it.enhancerSync: trueon an enhancer that meets the criteria above:enhanceCodeEmphasis,createEnhanceCodeEmphasis(...)) already set it.Compatibility
SourceEnhancer.enhancerSyncand a new subpath; no exports were removed or changed (checked against the.d.tsof 0.13.4-canary.18).CodeProviderLazyand no server enhancers, emphasis applies right after hydration, deterministically. To server-render the frames, and avoid a layout shift when the window applies, passsourceEnhancers={[enhanceCodeEmphasis]}toCodeHighlighteror use the eagerCodeProvider.Tests and verification
renderToStringthenhydrateRoot, lazy chunk loaded on one side only) for the loader path,useSourceEnhancing, custom and unmarked enhancers, and the client-rendered loading fallback; fallback-window, render-count and helper tests. They fail before the fix.code-highlighter/demos/code-inline-hydrationfails against a fresh canary.18next devand passes after.typescript,eslint,docs:build,docs:validate(0 changes on the second run) and the code-highlighter production e2e pass;demo-variantsis a known pre-existing flake.next devstarts gave 0 hydration warnings.