Load the OpenAPI renderer only on pages that use it - #4472
Draft
nolannbiron wants to merge 5 commits into
Draft
Conversation
🦋 Changeset detectedLatest commit: 0fb4e07 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
`/core` and `/light` were named after their weight rather than their contents, so nothing said where a new export belonged. Next's barrel optimizer reaches the same client entry (1519.8 KB) from plain barrel imports, and the package keeps a single public entry. Also renders the schemas block heading again: `OpenAPISchemas` asks the context for one, and only the operation heading was pre-rendered.
The block became a client component, so `generateSchemaExample` ran again while hydrating — and its date placeholders come from `new Date()` at module scope, which differs between the server's module instance and the browser's. Under `force-static` the two are hours apart, not milliseconds. Resolving now generates every example the renderer asks for and attaches it to the schema, keyed by variant. The browser reads those values instead of regenerating, so the date is only ever evaluated on the server.
nolannbiron
force-pushed
the
nolann/openapi-serializable-context
branch
from
August 6, 2026 07:27
54dda9d to
dcab45a
Compare
Contributor
|
The latest updates on your projects. Learn more about Argos notifications ↗︎
|
The client boundary made every clock read in the render path a hydration hazard. The date was the only one, so fixing it removes the need to carry generated examples through the payload.
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.
Pages with no OpenAPI block were still shipping the whole
@gitbook/react-openapiclient renderer — react-aria included — in the route entry. Next inlines a client module into the entry as soon as a server module reaches it through the graph, and tree-shaking never applies, so the only fix is to stop reaching it.Two changes, and neither works without the other:
'use client'component loaded throughnext/dynamic. Functions can't cross the RSC boundary, sorenderCodeBlockis rebuilt on the client, whilerenderHeading/renderDocumentare pre-rendered on the server and passed asReactNode, and the proxy URL is pre-signed into a string.experimental.optimizePackageImportsfor the package, which rewrites barrel imports into deep ones. Without it,context.tsxand theresolveOpenAPI*Block.tsfiles kept dragging the renderer in for the sake of four helpers.Measured
Total JS downloaded by
/url/mariadb.com/docs— a page with no OpenAPI block — on the Vercel previews, summing every chunk the HTML references.−208.7 KB (−9.7%), and react-aria leaves the page entirely. Neither PR gets that alone: #4468 removed the last non-OpenAPI importer (
FocusScope/usePreventScrollin the Mermaid block), this one removesreact-aria-componentsandreact-statelywith the renderer.The dynamic boundary on its own is a regression: built without
optimizePackageImports, the same page grows to 2162.2 KB against main's 2114.9 KB (measured locally, which reads ~4% low in absolute terms but is consistent across the three builds). The boundary makes the renderer extractable; the import rewrite is what stops the incidental imports from holding it in place.Two bugs this surfaced
Moving the block to the client changed which code runs in the browser, and both bugs come from that.
OpenAPISchemasasks the context for one, and only the operation heading was being pre-rendered.getSchemasHeadingis now exported so the renderer and the host reach the same answer from one place.date-timeexample.generateSchemaExamplebuilds its placeholders fromnew Date()at module scope, so the server's module instance and the browser's disagree. Underforce-staticthey are hours apart, not milliseconds. Examples are now generated once during resolution and attached to the schema keyed by variant; the browser reads them instead of regenerating, so the date is only ever evaluated on the server.precompute-examples.test.tslocks the invariant — it replays all six render call sites against JSON-round-tripped data and fails if anything regenerates.Trade-offs
optimizePackageImportsbails silently ifpackages/react-openapi/src/index.tsever stops being pure re-exports, which would quietly bring the 209 KB back.Not in scope
The OpenAPI stylesheet (~335 KB including Scalar) is still loaded on every page. Moving its import behind the dynamic boundary does remove it from the entry, but the App Router has no way to emit a
<link>for it during SSR, so blocks would render unstyled before hydration. Doing this properly needs the stylesheet emitted as a standalone asset and referenced from the server component — best done alongside #4464, which already builds that machinery for Scalar's JS.API
@gitbook/react-openapikeeps a single public entry;/coreand/lightwere tried and dropped in favour of the barrel optimizer. Theminorbump is for one new export,getSchemasHeading, and for the generated examples now present in resolved data.