`<head>` element ordering: `link[stylesheet]` should come before `link[modulepreload]`
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 15.1k
- Forks
- 1.9k
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 143
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
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.
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.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100