Question
Should CSS Come Before JavaScript in HTML? Rendering and Loading Explained
Question
In many places online, I have seen the recommendation that CSS files should be included before JavaScript files.
The usual explanation is that the browser's rendering thread needs the CSS first so it can render the page correctly. If JavaScript is loaded first, the browser must parse and execute that JavaScript before continuing to later resources, which supposedly delays rendering because the styles are not yet available.
However, my own testing seems to show something different.
I created a small Ruby server that adds configurable delays to CSS and JavaScript responses so I can test loading behavior:
require 'rubygems'
require 'eventmachine'
require 'evma_httpserver'
require 'date'
class Handler < EventMachine::Connection
include EventMachine::HttpServer
def process_http_request
resp = EventMachine::DelegatedHttpResponse.new(self)
return unless @http_query_string
path = @http_path_info
array = @http_query_string.split('&').map { |s| s.split('=') }.flatten
parsed = Hash[*array]
delay = parsed['delay'].to_i / 1000.0
jsdelay = parsed['jsdelay'].to_i
delay = 5 if delay > 5
jsdelay = 5000 if jsdelay > 5000
delay = 0 if delay < 0
jsdelay = 0 if jsdelay < 0
operation = proc do
sleep delay
if path.match(/.js$/)
resp.status = 200
resp.headers['Content-Type'] = 'text/javascript'
resp.content = "(function(){
var start = new Date();
while (new Date() - start < #{jsdelay}) {}
})();"
end
if path.match(/.css$/)
resp.status = 200
resp.headers['Content-Type'] = 'text/css'
resp.content = 'body { font-size: 50px; }'
end
end
callback = proc do |_res|
resp.send_response
end
EM.defer(operation, callback)
end
end
EventMachine.run do
EventMachine.start_server('0.0.0.0', 8081, Handler)
puts 'Listening...'
end
Then I used this HTML page for testing:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<script type="text/javascript">
var startTime = new Date();
</script>
<link
href="http://10.0.0.50:8081/test.css?delay=500"
type="text/css"
rel="stylesheet"
>
<script
type="text/javascript"
src="http://10.0.0.50:8081/test2.js?delay=400&jsdelay=1000"
></script>
</head>
<body>
<p>
Elapsed time is:
<script type="text/javascript">
document.write(new Date() - startTime);
</script>
</p>
</body>
When I load CSS first, the page renders in about 1.5 seconds.
When I load JavaScript first, the page renders in about 1.4 seconds.
I see similar results in Chrome, Firefox, and Internet Explorer. In Opera, the ordering does not seem to matter.
From this, it appears that JavaScript execution may wait for CSS to finish loading. If that is true, putting JavaScript first could sometimes be more efficient because the browser starts working on it earlier.
What am I missing here? Is the recommendation to place CSS before JavaScript not always correct?
I understand that other approaches exist, such as using async, moving scripts to the end of the page, deferring work with setTimeout, or using script loaders. My question is specifically about the order of essential CSS and essential JavaScript inside the <head>.
Short Answer
By the end of this page, you will understand why developers often place CSS before JavaScript in the <head>, what browser blocking behavior actually happens, and why timing tests can produce results that seem to contradict the common advice. You will also learn the practical rule: CSS usually goes first for correctness and perceived rendering, while JavaScript placement depends on whether the script is blocking, deferred, async, or dependent on computed styles.
Concept
Browsers do not load and render HTML in one simple straight line. They parse the document, discover external resources like stylesheets and scripts, and coordinate several kinds of work:
- HTML parsing
- CSS downloading and parsing
- JavaScript downloading and execution
- DOM construction
- CSSOM construction
- Layout and paint
A key beginner concept is this:
- A classic
<script src="...">in the document blocks HTML parsing while it is fetched and executed. - A stylesheet does not block HTML parsing in the same way, but it can block rendering and may also delay script execution in some cases.
Why can CSS affect JavaScript?
Because JavaScript can query style-related information, such as:
getComputedStyle(element)
element.offsetWidth
element.clientHeight
If the browser allowed a script to run before relevant CSS was ready, those values could be wrong. To avoid inconsistent behavior, browsers often make scripts wait for earlier stylesheets that could affect the page.
So the usual recommendation exists for two practical reasons:
- Render the page with correct styling as early as possible
- Avoid blocking interactions between stylesheets and synchronous scripts
Your experiment is useful because it reveals something real: script execution and stylesheet loading are not independent. In many browsers, a script that appears after a stylesheet may wait until that stylesheet is available.
Mental Model
Think of the browser as a stage crew preparing a theater scene.
- HTML is the script telling the crew what props and actors exist.
- CSS is the costume and stage design plan.
- JavaScript is the live director giving instructions during setup.
If the director starts giving instructions before the costume plan arrives, the crew may have to pause because those instructions might depend on what costumes or scenery are supposed to look like.
That is why browsers are careful. A script might ask, “How wide is this element?” But width depends on styles. So the browser may wait for CSS before letting the script continue.
Another important idea:
- Rendering a page is not just about finishing downloads fastest
- It is about getting to a correct, stable, visible page efficiently
So even if one resource order gives a slightly smaller raw timing number in a test, that does not automatically make it the better real-world choice.
Syntax and Examples
The resource order in the <head> commonly looks like this:
<head>
<link rel="stylesheet" href="styles.css">
<script src="app.js"></script>
</head>
This means:
- The browser discovers the stylesheet early.
- It starts downloading CSS.
- It later reaches the script.
- The script may wait for earlier CSS before executing.
Better modern pattern for many pages
<head>
<link rel="stylesheet" href="styles.css">
<script src="app.js" defer></script>
</head>
With defer:
Step by Step Execution
Consider this HTML:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles.css">
<script src="app.js"></script>
</head>
<body>
<h1>Hello</h1>
</body>
</html>
Assume:
styles.csstakes 500 ms to downloadapp.jstakes 400 ms to downloadapp.jsruns for 1000 ms
What typically happens
- The browser starts parsing HTML.
- It sees
<link rel="stylesheet">. - The browser starts downloading
styles.css. - Parsing continues until the browser sees .
Real World Use Cases
This concept matters in real applications because front-end performance is rarely about only one file loading faster.
1. Marketing pages and landing pages
These pages need a fast, styled first paint. If CSS is delayed, users may see unstyled content or layout jumps.
2. Dashboards and admin tools
Scripts often read element sizes, initialize charts, or attach UI behavior. If styles are not ready, measurements can be wrong.
3. Component libraries
A modal, tooltip, or grid system may depend on CSS classes for sizing and visibility. Loading order affects whether JavaScript initializes correctly.
4. E-commerce product pages
Critical styles must load early so the page structure is stable. JavaScript for carts, analytics, and widgets should ideally not block initial rendering.
5. Single-page apps
Even app shells benefit from early CSS. Bundled JavaScript is often large, so using defer or modern build tooling is more important than simply swapping CSS and JS order.
6. Server-rendered apps
Pages generated on the server often have visible content immediately. Early CSS helps make that content usable and visually correct before JavaScript hydration or enhancement runs.
Real Codebase Usage
In real codebases, developers usually follow a few patterns instead of debating only “CSS first or JS first?”.
Common practical pattern
<head>
<link rel="stylesheet" href="/assets/app.css">
<script src="/assets/app.js" defer></script>
</head>
Why this is common:
- CSS is discovered early.
- JavaScript does not block parsing.
- The DOM is ready before the script runs.
Guard clauses for DOM-dependent code
Developers often protect code that expects certain elements:
const menu = document.querySelector('.menu');
if (!menu) return;
menu.addEventListener('click', () => {
console.log('menu clicked');
});
This helps when scripts are shared across many pages.
Waiting for the DOM
Common Mistakes
1. Treating “CSS before JavaScript” as an absolute rule
It is a useful default, not a law of physics.
- Blocking CSS generally belongs early.
- But
defer,async, inline critical CSS, and app architecture can change the best choice.
2. Measuring the wrong thing
Beginners often time “when a script writes text” and assume that equals page performance.
That can miss:
- first paint
- styled paint
- layout shifts
- time to interactive
3. Putting blocking scripts in the head unnecessarily
<head>
<link rel="stylesheet" href="styles.css">
<script src="huge-library.js"></script>
</head>
If the script is not required immediately, this slows parsing and often hurts the page more than the CSS order itself.
4. Using async when order matters
Broken example:
Comparisons
| Situation | Behavior | Good use case | Risk |
|---|---|---|---|
| CSS before blocking script | Styles discovered early; script may wait for CSS | Traditional pages where styling correctness matters | Script may not start immediately |
| Blocking script before CSS | Script starts earlier | Rare cases where script must run immediately and does not depend on styles | CSS discovered later; slower styled render |
CSS + defer script | CSS early, script downloads in parallel, runs after parsing | Best default for many modern pages | Script still waits until parsing completes |
CSS + async script | Script downloads in parallel and runs as soon as ready | Independent scripts like analytics | Execution order is not guaranteed |
| CSS in head + script at end of body | Early styles, late script execution |
Cheat Sheet
Default rule
- Put stylesheets early in the
<head>. - Avoid blocking scripts in the
<head>when possible. - Prefer
deferfor main JavaScript files.
Safe modern pattern
<head>
<link rel="stylesheet" href="styles.css">
<script src="app.js" defer></script>
</head>
Key rules
- Classic
<script src="...">blocks HTML parsing. - Stylesheets can block rendering.
- Scripts may wait for earlier stylesheets before executing.
- A timing test can be misleading if it does not measure paint and layout stability.
Use defer when
- the script depends on the DOM
- script order matters
- you want parsing to continue
Use async when
FAQ
Does CSS always need to come before JavaScript?
No. It is not an absolute requirement. But for blocking resources in the <head>, placing CSS before JavaScript is usually the best default.
Why can JavaScript wait for CSS?
Because JavaScript may read style-dependent values such as computed styles or element dimensions. Browsers often wait to ensure those values are correct.
If JavaScript first benchmarks faster, should I use that order?
Not automatically. A faster benchmark for one measurement may still produce worse rendering, delayed styling, or layout shifts for users.
Is defer better than moving scripts to the bottom of the body?
Often yes. defer keeps scripts discoverable early while avoiding parser blocking, and it preserves script order.
When should I use async instead of defer?
Use async for independent scripts that do not depend on DOM readiness or other scripts, such as analytics.
Does CSS block HTML parsing?
Not in the same direct way as a classic script. But CSS can block rendering and can also delay later script execution.
Why is CSS-first still recommended if browsers are smart about parallel loading?
Because early CSS discovery improves the chance of an early, correct, styled render, which usually matters more than tiny differences in synthetic timing.
What is the best general pattern for modern web pages?
Mini Project
Description
Build a small HTML page that compares three loading patterns: a blocking script in the head, a deferred script in the head, and a script placed at the end of the body. This project demonstrates how resource order affects parsing, rendering, and DOM availability.
Goal
Create a page that logs when CSS is loaded, when JavaScript runs, and when the DOM is ready, so you can observe the effect of different script-loading strategies.
Requirements
- Create one HTML page with a linked stylesheet and a JavaScript file.
- Add visible content that should be styled by the CSS.
- Log timestamps for script execution and
DOMContentLoaded. - Test one version with a blocking script, one with
defer, and one with the script before</body>. - Observe whether the script can access DOM elements and whether the page styles appear promptly.
Keep learning
Related questions
Angular ngClass Conditional Class Binding Explained
Learn how to use Angular ngClass for conditional classes, fix common binding mistakes, and understand why this template error happens.
CSS :not() Selector for Elements Without a Class or Attribute
Learn how to use the CSS :not() selector to target elements that do not have a specific class or attribute, with examples and common pitfalls.
CSS Font Scaling Relative to Container Size: %, em, rem, vw, and Responsive Text
Learn how CSS font scaling really works and how to make text responsive using %, em, rem, vw, clamp(), and media queries.