Skip to content

[Fizz] Fix duplicate preload links in SSR#36249

Open
Shramkoweb wants to merge 1 commit intofacebook:mainfrom
Shramkoweb:fix/rsc-preload-duplicate-35889
Open

[Fizz] Fix duplicate preload links in SSR#36249
Shramkoweb wants to merge 1 commit intofacebook:mainfrom
Shramkoweb:fix/rsc-preload-duplicate-35889

Conversation

@Shramkoweb
Copy link
Copy Markdown

Summary

Fixes #35889

<link rel="preload" as="font" /> (and other preload types) rendered inside an RSC component appears twice in SSR HTML when used with Fizz SSR. Regression since 19.2.0, introduced in #34604.

Root cause:

When the RSC Flight server processes a <link rel="preload"> element, it does two things:

  1. Emits a Flight hint ('L' row) via processLink() in ReactFlightServerConfigDOM.js
  2. Includes the <link> element in the serialized component output

During Fizz SSR, the Flight hint is received and dispatched to the Fizz preload() function, which registers the resource in resumableState and adds it to renderState.fontPreloads. Later, the same <link> element from the component output is processed by pushLink(), which fell through to pushLinkImpl(renderState.hoistableChunks, props) with no deduplication check — producing a second identical link tag.

Fix:

In pushLink(), before emitting a <link rel="preload"> to hoistableChunks, check whether the resource is already registered in resumableState (using the same per-as-type tracking that preload() uses: imageResources, styleResources, scriptResources, and unknownResources for fonts and others). If already registered, return null. If not yet registered, register it so future preload() calls will also deduplicate correctly.

How did you test this change?

Added regression test does not emit duplicate preload links in SSR when Flight hints preload a resource that is also rendered as JSX in ReactFlightDOM-test.js that renders a <link rel="preload" as="font"> inside an RSC component through Flight → Fizz SSR and asserts the link appears exactly once in the HTML output.

… rendered resource (facebook#35889)

When Flight encounters a `<link rel="preload">` JSX element during server
rendering, it emits a preload hint while still serializing the element in
the payload. During SSR, Fizz processes both the hint (via `preload()`)
and the element (via `pushLink()`), resulting in duplicate `<link>` tags
in the HTML output.

Add deduplication in `pushLink()` for `rel="preload"` links by checking
and registering in `resumableState` before emitting, matching the same
resource tracking used by the imperative `preload()` API.
@meta-cla
Copy link
Copy Markdown

meta-cla bot commented Apr 10, 2026

Hi @Shramkoweb!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@meta-cla meta-cla bot added the CLA Signed label Apr 10, 2026
@meta-cla
Copy link
Copy Markdown

meta-cla bot commented Apr 10, 2026

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: <link rel="preload" as="font" ...> in RSC gets duplicated in SSR html

1 participant