Uh oh!
There was an error while loading. Please reload this page.
fix(docs): give Fumadocs the public pathname on default-locale routes - #73
Merged
Merged
Conversation
Every unprefixed docs page logged React #418 (hydration mismatch) in the browser console; `/zh-Hans/…` and `/ja/…` were clean. `hideLocale: 'default-locale'` keeps English out of the public URL, so `/docs/quickstart` is served by rewriting to the `/en/docs/quickstart` route. The two renderers then see different pathnames for one page: the build-time prerender knows the internal route, the browser knows the public URL. Fumadocs compares that pathname against page-tree `url`s, which are in the public space, so the server looked the current page up and missed — no active sidebar item, no expanded folder, no page title in the TOC trigger, no prev/next footer — while the client rendered all four. Prefixed locales are unaffected because their internal and public paths are identical, which is exactly the recorded asymmetry. Normalise the pathname at `FrameworkProvider`, the single point where one enters Fumadocs, so both renderers agree on the public URL. This needs the provider assembled by hand: `fumadocs-ui/provider/next` wraps `RootProvider` in its own `FrameworkProvider`, which would take precedence over one layered outside it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CJPxtTxoxTUnjNdTbiEaRa
os-zhuang
marked this pull request as ready for review
August 18, 2026 09:51
This was referenced Aug 18, 2026
Uh oh!
There was an error while loading. Please reload this page.
os-zhuang
commented
Aug 18, 2026
ContributorAuthor
Merged as Updated from Verified on Generated by Claude Code |
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 freeto 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.
Fixes#61
What was wrong
Every unprefixed docs page logged one React #418 (hydration mismatch) in the browser console;
/zh-Hans/…and/ja/…were clean. Reproduced on a production build before touching anything.The pointer in the issue comment — the
hideLocale: 'default-locale'rewrite — was the right place to look, and the mechanism underneath it is this:hideLocale: 'default-locale'keeps English out of the public URL, so/docs/quickstartis served by rewriting to the/en/docs/quickstartroute. The two renderers then observe different pathnames for one page:usePathname()/en/docs/quickstart/docs/quickstartFumadocs compares that pathname against the
urlof every page-tree node, and those are produced byloader({ i18n })in the public space (/docs/quickstart, locale hidden). So on the serversearchPath()looked the current page up in the tree and missed, and the page rendered as if no page were current — no active sidebar item, no expanded folder, no page title in the TOC trigger, no prev/next footer. The client, at the public URL, rendered all four. React discarded the subtree and re-rendered it.Prefixed locales are unaffected because their internal and public paths are identical — which is exactly the asymmetry recorded on the issue.
Measured directly, same build, same page, server-rendered HTML:
The only difference between those two is the
/enprefix the browser never sees.The fix
FrameworkProvideris the single point at which a pathname enters Fumadocs — all 19 of its modules that need one read it throughusePathnamefromfumadocs-core/framework, andfumadocs-core/framework/nextis the only place Next'susePathnameis injected. Normalising it there fixes every consumer at once.app/[lang]/root-provider.tsxwiresRootProviderto Next exactly asfumadocs-ui/provider/nextdoes, except theusePathnamehanded to Fumadocs reports the public URL. The provider is assembled by hand rather than imported becausefumadocs-ui/provider/nextwrapsRootProviderin its ownFrameworkProvider, which would take precedence over one layered outside it.toPublicPathname()mirrors Fumadocs' ownhideLocaleURL rules so the two stay in agreement:neverkeeps every prefix,default-localehides only the default language,alwayshides every supported one. Only a whole leading segment that is a supported locale is removed, so a page such as/end-to-endis left alone.Nothing is suppressed: no
suppressHydrationWarning, no change tohideLocale, no consumer-side tolerance. The server now computes the same value the client does, so there is no client re-render left to hide.Verification
Production build (
pnpm --filter @objectos/docs build, thennext start), driven with headless Chromium,pageerror+ console captured. Both directions, same build pipeline before and after.Before — the six unprefixed routes named on the card:
(The two argument shapes line up with the mechanism: top-level pages lost the TOC trigger's page title — a text node; pages inside a folder additionally lost the expanded folder and the prev/next footer — HTML structure.)
After, at
d9ab7f4:Plus
/,/privacy,/terms,/docs/deploy/docker,/de/docs/quickstart,/ko/docs,/zh-Hans/privacy— all clean. Redirects unchanged:/en/docs/quickstart→ 307/docs/quickstart,/cn/docs/quickstart→ 308/zh-Hans/docs/quickstart,/→ 307/docs.The mechanism, not just the symptom — server-rendered HTML for the same page, before vs after:
Affordances exercised in the browser after the fix, on unprefixed routes: sidebar click navigates client-side (0 full page loads,
next/linkstill wired) and the active item follows; the language switcher goes/docs/architecture→/zh-Hans/docs/architecture; the theme toggle flipslight→dark. Zero console errors across the whole interactive run.Gates, at the tree of
d9ab7f4:Both translation scripts were run because they parse
apps/docs/lib/i18n.tsand a sibling card is editing them. This change does not touchlib/i18n.ts, so thelanguages: [...]shape they regex over is untouched; the runs confirm it. No locale-tagged.mdxsibling is touched.Note for upstream
Any Fumadocs site combining
hideLocalewith prerendered routes has this,createI18nMiddlewareincluded — itsdefault-localebranch performs the same rewrite, andframework/nextpasses Next'susePathnamethrough unmodified. Worth reporting upstream; the fix here does not depend on that landing.Generated by Claude Code