HTML and CSS
browser
web resources
loading
priority
web performance
This lesson is not about parsing HTML or the visual rendering of a website. Here, we focus specifically on how the browser manages HTTP requests for external resources (like stylesheets, images, or scripts) after building the DOM. Understanding this process is key to diagnosing and optimizing website loading, especially when performance issues arise.
We assume you already know:
Now we dive deeper: what happens to all those resources referenced in the HTML? Does the browser download them all at once? What does it prioritize? What can block rendering? Let’s find out!
Once the browser has finished parsing the HTML and building the DOM, its work is far from over. It now needs to load all the additional resources mentioned in the page to make it look and function correctly. These resources include: Stylesheets (CSS), images, JavaScript scripts, custom fonts, videos, or other media files.
The question is, does it load them all at once? What happens if there are too many? This is where resource prioritization comes into play. The browser detects various tags in the HTML that indicate external resources:
<link>
→ CSS<img>
→ images<script>
→ JavaScript<video>
, <audio>
, <source>
→ media@font-face
→ custom fontsEach of these resources requires an additional request to be downloaded. But the browser doesn’t do this in any order—it prioritizes what is essential to display the page correctly.
Browsers follow certain priority rules:
CSS first: Stylesheets block page rendering because they affect how it looks. The browser won’t display anything until the CSS is ready.
JavaScript after CSS: Scripts can also block rendering if they are in the <head>
and don’t use defer
or async
.
Images and other visual resources: These load in the background, once the main content is being processed.
When the browser finishes loading all the main resources referenced in the page (HTML, CSS, images, scripts, fonts, etc.), it triggers a very important event: the onload event. This event indicates that the DOM is fully built and all necessary external resources are completely downloaded.
In JavaScript, you can use it like this:
1window.onload = function() { 2 console.log('The entire page has loaded'); 3 // Safe to manipulate images, applied styles, etc. 4};
Note:
onload
is not immediate; it occurs after everything is downloaded, including images and videos.
For this reason, you shouldn’t use onload
for tasks that need to run as soon as possible (e.g., initializing basic user interaction). Instead, use it for tasks that depend on everything being visible and available.
Before the onload
event occurs, browsers already intelligently handle when to load and execute JavaScript scripts.
async
: Downloads the script in parallel and executes it as soon as it’s ready, without waiting for the rest of the HTML. This can make script execution unpredictable.
defer
: Downloads the script in parallel but waits until the HTML is fully parsed before executing it, without blocking document parsing.
In other words, async
and defer
occur before onload
and help speed up the initial page construction without affecting the final download of all resources. Understanding async
, defer
, and onload
gives you control over when and how your JavaScript runs in relation to the page loading process.
Because it directly affects page load time. If the browser waits too long to load a key resource (like blocking CSS or a large script), the user will see a blank screen for longer.
Knowing this allows you to:
defer
, async
, loading="lazy"
If we compare this process to a professional kitchen, it would look something like this:
The browser works the same way: it loads the essentials first, and the decorative elements later.
flowchart TD A[Browser finishes reading the HTML] --> B[Detects links to external resources] B --> C{Is it CSS?} C -- Yes --> D[Load immediately and block rendering] C -- No --> E{Is it JS in <head>?} E -- Yes --> F[Execute before continuing] E -- No --> G{Is it an image or media resource?} G -- Yes --> H[Load in the background] H --> I[Display page to the user] F --> I D --> I
In the world of web performance, there’s a key concept: above the fold and below the fold.
Why does this distinction matter? Because the browser and developers can prioritize resources that affect first the visible part (above the fold
) to give the impression that the page loads quickly, even if there’s still content downloading below.
loading="lazy"
and Its Relationship with the FoldTo improve this behavior, there’s a very useful attribute for images: loading="lazy"
. When we add loading="lazy"
to an image:
This is perfect for images that are below the fold because it saves initial bandwidth, speeds up the construction of the visible DOM, and reduces the time until the user sees something useful.
1<img src="heavy-photo.jpg" loading="lazy" alt="An image that appears on scroll">
Note: Images above the fold SHOULD NOT have
loading="lazy"
, because the user needs to see them right away.
Yes. To achieve faster loading, it’s a good practice to:
async
, defer
, or lazy-loaded).For example:
This strategy helps users see and use the site much faster, even if the total page load isn’t complete yet.
These concepts are fundamental for identifying and solving slow-loading issues:
Blank screens for several seconds: Could be due to blocking CSS or critical images with loading="lazy"
applied incorrectly.
High time to first render (low TTFB but blank screen): Often indicates issues with the loading order of CSS or JavaScript.
Frozen or slow scrolling: Likely caused by too many heavy below the fold
resources that aren’t optimized or deferred.
Visible images appearing late or never: They might have loading="lazy"
but are above the fold (misuse of lazy-loading).
Correct visible rendering but functional failures (buttons, forms): The JavaScript needed for interaction was deferred or loaded too late.