diff --git a/.changeset/quiet-script-preloads.md b/.changeset/quiet-script-preloads.md
new file mode 100644
index 0000000000..bf5474ed1d
--- /dev/null
+++ b/.changeset/quiet-script-preloads.md
@@ -0,0 +1,5 @@
+---
+'@tanstack/router-core': patch
+---
+
+Render manifest script preloads with low fetch priority so they do not compete with render-blocking resources.
diff --git a/packages/react-router/tests/Scripts.test.tsx b/packages/react-router/tests/Scripts.test.tsx
index 893cef01bc..976410cba5 100644
--- a/packages/react-router/tests/Scripts.test.tsx
+++ b/packages/react-router/tests/Scripts.test.tsx
@@ -547,6 +547,35 @@ describe('ssr HeadContent', () => {
)
})
+ test('renders manifest module preloads with low fetch priority', async () => {
+ const preloadHref = '/low-priority-module.js'
+ const rootRoute = createRootRoute({
+ component: () => ,
+ })
+ const indexRoute = createRoute({
+ path: '/',
+ getParentRoute: () => rootRoute,
+ })
+ const router = createRouter({
+ history: createMemoryHistory({ initialEntries: ['/'] }),
+ routeTree: rootRoute.addChildren([indexRoute]),
+ isServer: true,
+ })
+
+ router.ssr = {
+ manifest: createTestManifest(rootRoute.id, { preloadHref }),
+ }
+
+ await router.load()
+
+ const html = ReactDOMServer.renderToString(
+ ,
+ )
+ expect(html).toContain(
+ ``,
+ )
+ })
+
test('keeps manifest stylesheet links mounted when history state changes', async () => {
const history = createTestBrowserHistory()
const stylesheetHref = '/history-state.css'
@@ -745,7 +774,7 @@ describe('ssr HeadContent', () => {
).toBe('.runtime{color:red}')
})
- test('renders preload as script links for iife manifest preloads', async () => {
+ test('renders iife manifest preloads as low priority script links', async () => {
const history = createTestBrowserHistory()
const preloadHref = '/iife-preload.js'
@@ -795,6 +824,13 @@ describe('ssr HeadContent', () => {
`link[rel="modulepreload"][href="${preloadHref}"]`,
),
).toBeFalsy()
+ expect(
+ document.head
+ .querySelector(
+ `link[rel="preload"][as="script"][href="${preloadHref}"]`,
+ )
+ ?.getAttribute('fetchpriority'),
+ ).toBe('low')
})
test('assetCrossOrigin overrides manifest crossOrigin values', async () => {
diff --git a/packages/router-core/src/manifest.ts b/packages/router-core/src/manifest.ts
index aca4b96eac..b43f485236 100644
--- a/packages/router-core/src/manifest.ts
+++ b/packages/router-core/src/manifest.ts
@@ -43,6 +43,7 @@ export function getScriptPreloadAttrs(
rel: 'modulepreload' | 'preload'
as?: 'script'
href: string
+ fetchPriority: 'low'
crossOrigin?: AssetCrossOrigin
} {
const preloadLink = resolveManifestAssetLink(link)
@@ -54,6 +55,7 @@ export function getScriptPreloadAttrs(
? { rel: 'preload', as: 'script' }
: { rel: 'modulepreload' }),
href: preloadLink.href,
+ fetchPriority: 'low',
...(crossOrigin ? { crossOrigin } : {}),
}
}
diff --git a/packages/vue-router/tests/Scripts.test.tsx b/packages/vue-router/tests/Scripts.test.tsx
index 8d6724076b..ad6da944f1 100644
--- a/packages/vue-router/tests/Scripts.test.tsx
+++ b/packages/vue-router/tests/Scripts.test.tsx
@@ -311,6 +311,7 @@ describe('ssr HeadContent', () => {
const ssrPreload = document.createElement('link')
ssrPreload.setAttribute('rel', 'modulepreload')
ssrPreload.setAttribute('href', '/main.js')
+ ssrPreload.setAttribute('fetchpriority', 'low')
document.head.append(ssrStylesheet, ssrPreload)
@@ -397,6 +398,7 @@ describe('ssr HeadContent', () => {
const ssrPreload = document.createElement('link')
ssrPreload.setAttribute('rel', 'modulepreload')
ssrPreload.setAttribute('href', '/index.js')
+ ssrPreload.setAttribute('fetchpriority', 'low')
document.head.append(ssrStylesheet, ssrPreload)