`<head>` element ordering: `link[stylesheet]` should come before `link[modulepreload]`
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 72/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Quiet
- Tech stack
- react, typescript
- Domain
- frontend
Research direction
Start in headContentUtils.tsx and trace how preloadLinks and links are assembled into the rendered head. Verify the current SSR output and change the ordering so stylesheet links appear before modulepreload links. Done means the generated head consistently uses that order and the reported HTTP/1.1 waterfall no longer discovers CSS after modulepreload links.
Written by the indexing model from the issue text.
Description
Which project does this relate to?
Start
Describe the bug
In headContentUtils.tsx, preloadLinks are placed before links, which means <link rel="modulepreload"> elements appear before <link rel="stylesheet"> in the rendered <head>.
Browsers fetch resources top-to-bottom. When modulepreload links come first, the render-blocking CSS resources needed to display styled SSR HTML get discovered late. Under HTTP/1.1's 6-connection-per-host limit, the CSS request can't even start until earlier JS downloads free up a connection.
Key result (Using a slow 4G throttle + 22 slow modulepreload links):
| Scenario | FCP | Δ |
|---|---|---|
| HTTP/1.1 — modulepreload first | 2075ms | |
| HTTP/1.1 — stylesheet first | 1260ms | −815ms (39%) |
| HTTP/2 — modulepreload first | 1388ms | |
| HTTP/2 — stylesheet first | 1380ms | −8ms (~1%) |
ℹ️ Happy to open a PR if this is something you'd like to see fixed. 🚀
Your Example Website or App
https://github.com/steschi/html-head-order-matters
Steps to Reproduce the Bug or Issue
- Create a TanStack Router SSR app with multiple route chunks (so Vite emits many modulepreload links) as well as a stylesheet import.
- Build and serve with
vite preview(HTTP/1.1) - Open Chrome DevTools → Network tab, throttle to Slow 4G
- Load the page and observe the waterfall: modulepreload fetches start before the stylesheet fetch
- The stylesheet is blocked behind the 6-connection limit, delaying FCP
Expected behavior
<link rel="stylesheet"> should appear before <link rel="modulepreload"> in the <head>, so the browser discovers and fetches render-blocking CSS first. This matches the capo.js recommended element ordering (stylesheets = weight 5, modulepreload = weight 4).
Screenshots or Videos
See the full reproduction repo for waterfall charts and Chrome DevTools traces:
https://github.com/steschi/html-head-order-matters
Platform
- Router / Start Version: 1.162.4 with nitro v3
- OS: macOS
- Browser: Chrome
- Browser Version: Chrome 145
Additional context
- capo.js —
<head>ordering audit tool by Rick Viscomi (Google) - Harry Roberts — "Get Your Head Straight" — talk on
<head>ordering and Core Web Vitals
- Dominant language
- TypeScript
- Stars
- 15.1k
- Forks
- 1.9k
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 136
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from TanStack/router
-
information needed
Difficulty 1/5 Under an hour Newbie friendliness 90/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
lazyRouteComponent reload guard key collides on Safari, capping stale-deploy recovery at one per tab Open
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
information needed
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Similar issues
-
calcite-components needs triage refactor
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Esri/calcite-design-system#15203 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 91/100
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficulty 1/5 Under an hour Newbie friendliness 95/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Automattic/studio#4908 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100