Skip to main content

Scripts in head

1. How the browser loads a page

When the browser receives an HTML document, it performs these steps:

  1. Parses the HTML line by line, top to bottom.
  2. When it hits a <link> tag (CSS), it stops and loads the stylesheet, because styles affect rendering.
  3. When it hits a <script> tag, it stops parsing in order to run the JS.

Why? Because a script can change the DOM itself, or even remove elements that come later in the HTML (document.write, innerHTML, and so on). So the browser has to wait for the JS to finish running before continuing to parse.


2. What happens if script is in head

javascript
<head> <script src="main.js"></script> </head> <body> <h1>Hello!</h1> </body>

Step by step:

  1. The browser starts reading the HTML.
  2. It reaches <script src="main.js">.
  3. It stops parsing the HTML.
  4. It waits for main.js to load over the network (can take hundreds of milliseconds).
  5. It runs the script.
  6. Only then does it continue parsing the rest of the page.

During all that time:

  • the DOM has not been built yet,
  • the user sees no content,
  • the FCP / LCP metrics suffer.

3. Problems this causes

ProblemWhat happens
The HTML parser is blockedThe page does not build until the JS loads
A long "white page"The user sees emptiness
Slow networks -> worse UXEspecially on 3G/4G
Worse Core Web VitalsLCP, FCP, TTI, TBT all grow
Scripts get in the way of critical CSSCritical styles do not apply in time

4. How to avoid blocking

Way 1. defer

javascript
<script src="main.js" defer></script>

What it does:

  • The script loads in parallel with the HTML,
  • but only runs after the DOM is fully parsed (before DOMContentLoaded).

No blocking. Guaranteed execution order (in sequence). Ideal for most cases.


Way 2. async

javascript
<script src="analytics.js" async></script>

What it does:

  • The script loads asynchronously (in the background),
  • and runs as soon as it loads, without waiting for others.

Used for independent scripts (analytics, ads, pixels). Execution order is not guaranteed.


Way 3. Moving scripts to the end of body

javascript
<body> ... <script src="main.js"></script> </body>

By the time the JS loads:

  • the HTML is fully parsed,
  • the user already sees content,
  • the JS runs as the last step.

This is the "old" but working way. In modern HTML, <script defer> has replaced it.


5. How to handle it in production

Script typeWhere and how to include it
Main logic (UI, SPA)<script src="main.js" defer>
Analytics, ads<script src="ga.js" async>
Inline initialization<script> inside <head> (small initialization)
Legacy librariesBetter to move to the end of <body>

6. Impact on metrics

MetricWhat gets worse with scripts in head
FCP (First Contentful Paint)The first content shows up later
LCP (Largest Contentful Paint)The main element renders later
TTI (Time to Interactive)JS blocks the main thread
TBT (Total Blocking Time)Grows because of heavy synchronous scripts

7. Illustration (in words)

javascript
HTML: <head> -> <script> -> <body> ↓ loading [======= JS is loading =======] ↓ execution [======= JS is running =======] ↓ only then is the DOM built

With defer, loading happens in parallel, and execution happens at the end:

javascript
HTML and JS load together -> DOM built -> JS runs

Short summary

ScenarioWhat it doesConsequence
<script> in head with no attributesBlocks parsingSlow
<script defer>Loads in parallel, runs after the DOMOptimal
<script async>Loads and runs independentlyOnly for independent scripts
<script> at the bottom of bodyWaits until the end of the HTMLNo blocking

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.