When you first set off in building a website or application, your index.html
file is as performant as you will ever get.
Yes, you can set up caching, HTTP/2, and other server-side optimizations, but ultimately, you’ll be serving an index.html
file to your users that starts out empty and gradually builds up over time as you ship features, fix bugs, add third-party scripts, and adjust a host of other interesting things that come over the lifetime of a project.
All this change can result in a “death by a thousand cuts” for the performance of your site. The pages start to load slower, it feels laggy, and your user engagement drops. You need to find some ways to get back to the snappy performance you once had, but how?
One of the ways you can improve the performance of your site is by inlining your CSS. This article explains the “what” and the “how” of inlining CSS by asking and answering a series of questions.
Before we get into what inlining CSS means and how to do it, it’s important to first understand how CSS can affect performance. While JavaScript and images generally play a much larger role in negatively impacting performance metrics, CSS can also play a significant role.
When a browser visits a URL, the first thing it gets back from the server is the HTML file. It then begins parsing this file, stopping to request any linked dependencies that it finds along the way. It checks the document’s <head>
first, looking for anything necessary before beginning into the <body>
to render the page.
CSS is treated as a render-blocking resource — that is, if you include any form of <link href="style.css" rel="stylesheet">
in the document’s <head>
, the browser will trigger an additional request to the server to retrieve that stylesheet before even beginning to render your page for the user.
While our internet providers, infrastructure, and servers are all getting faster every year, there is no such thing as a free lunch. Every network request to the server has a time cost, and the requests required to complete before the browser begins rendering the page are the most expensive.
In its simplest form, inlining CSS is an approach to avoiding or deferring this second (or more) network request from being required before the user can begin to visually see anything on the page.
Before trying to improve anything in regards to performance on your site, it’s important to measure the current performance first. Pick some metrics to measure (there are some good ideas of what here) and get a baseline of how your site is performing.
While all the content in this article and others is theoretically true, things always work differently in practice. So measure first, try some things, measure again, and if it’s not giving you the results you expected, then revert. If you measure positive results after implementing some changes, then you have a nice success story to share with your team or manager.
Inlining is the practice of integrating a portion of code directly into the place where it will be used, eliminating the need for the computer to do a function call or some other type of lookup. This makes the code faster.
While the inlining terminology originally comes from the C programming language, the concept and the name have found their ways to web development. In the case of CSS, the performance gain comes from inlining styles directly into the HTML document, eliminating the need for the browser to do a network request before getting the render-blocking styles.
If you conduct any research on inlining CSS, you might find some suggestions to inline styles in this way:
<p style="font-size: 20px; font-weight: bold;">Some text</p>
This is indeed true inlining, as there is no need for a network request to fetch an external style sheet or even the need for the browser to apply a CSS class name to the element. However, you should almost never inline CSS in this way as it ironically introduces certain performance losses when runtime responsiveness is the unit of measure.
Here are some reasons that you should almost always avoid inlining CSS in this way:
This goes back to the concept of CSS being a render-blocking resource. While this is commonly considered in the context of network requests, it can also be the amount of time the browser takes to parse through and understand all the CSS.
Say you’d like to avoid loading an external CSS stylesheet and therefore inline each element in the DOM individually. You’ll likely end up with many duplicated styles that could either be atomic CSS class names or regular class names for a certain type of element. As one developer’s benchmark indicates, inline-styling every HTML element significantly affects the time to first contentful paint.
Banging the drum of “don’t repeat yourself” is neither necessary nor helpful in every case, but for inlining CSS, you will save yourself a lot of headaches if every element isn’t styled individually. Yes, there are ways you could define styles as a reusable object and spread those styles into each element, but at some point, having each element individually styled is going make a future refactor more difficult than it needs to be.
If duplication of styles wasn’t enough warning to “turn back now,” then this is a good time to remember that inline styles have high specificity and therefore can only be overwritten by use of the !important
declaration.
For the sake of painting a complete picture, inlining CSS in this way is not always harmful to performance or maintainability.
Imagine some JavaScript is adding or removing elements dynamically from the DOM as the user interacts with the site. In this case, the browser has already finished its downloading, parsing, and rendering tasks, and performance is (mostly) no longer an issue. But in the case for inlining CSS that will be present in the DOM when the browser loads the page, inlining in this way should be avoided.
While the previous section discouraged a form of inlining CSS, there is another method of inlining that is much more performant in terms of runtime responsiveness. Instead of linking to a external CSS file like this:
<head> ... <link href="styles.css" rel="stylesheet"> </head>
You can directly include the styles (or a portion of them) in the document head like this:
<head> ... <style type="text/css"> body{background:#fff;color:#000;margin:0}.link{color:#1a0dab}.ts{border-collapse:collapse}.ts td{padding:0}.g{line-height:1.2;text-align:left;width:600px}.ti{display:inline;display:inline-table} </style> </head>
This solves the problem of requiring the browser to send additional network requests before being able to render the page and also solves the problem of trying to inline-style each individual element. We can now style all the links in the page by just giving the element a link class <a href="some-link" class="link">Some link</a>
as you might normally do.
Success! We’re now able to reduce the number of network requests while giving the DOM the styles it needs to render as quickly as possible. All good, right?
Almost.
Software development is all about tradeoffs. In the pursuit of better performance, we need to determine what tradeoff we’re willing to make. We eliminated a network request (good) but we just transferred those kilobytes to the HTML file and made it a bigger download (bad).
There’s no such thing as a free lunch! There is some balance to be made in accepting the overhead of having a larger HTML file in order to eliminate a synchronous network request, but at some point, if you put too many styles into the head, your performance metrics will actually suffer.
The observation to make here is that inlining CSS makes things more performant for metrics such as first contentful paint when we only inline critical CSS in the document head (with the key word being critical). Your site’s stylesheet might contain many, many more styles that the user will see when they first load your page.
For example, the styles for your footer will not be necessary when the browser first paints the site. Maybe only the header, hero, and some content styles are needed for that first paint. What is displayed in the browser’s viewport on the initial load is what can be considered as “critical”.
Going back to the discussion of tradeoffs, eliminating network requests, and making sure the HTML file doesn’t get too big, it’s worth mentioning that there is some advice given around the idea of having a size limit of 14KB for all the critical resources for your page. The following three links are helpful in case you’d like to dig deeper into this idea:
Ultimately, it’s important in terms of inlining CSS and performance that you don’t just dump all the CSS for your site into the head. If you inline too much, the performance implications will be worse than what you started with.
So we’ve now seen that inlining critical CSS can have a positive performance impact for our site. We’ve removed the link to the external stylesheet from the document head, figured out what our “critical” CSS is, and included it as an inline style in the head, but the rest of the page still needs styles. How can we style the rest of the page?
We can save the rest of the (non-critical) styles for the page in an external stylesheet and defer its download until the browser finishes with the render-blocking work, like this:
<head> ... <style type="text/css"> /* inlined styles */ </style> <link rel="stylesheet" href="styles.css" media="print" onload="this.media='all'"> </head>
Note that it is also possible to request the external stylesheet with JavaScript and add it to the DOM. However, by using a traditional link, the browser is able to store the response in its memory cache, which is something more difficult for a JavaScript solution to achieve. If the media type print in the link reads strangely to you, I’d encourage you to read this article, which explains how this works and how this approach is even more preferable than using a link prefetch.
With that, we now have all the styles necessary for the whole page but with the critical styles inlined and the non-critical loaded asynchronously shortly afterwards. The only difference your users will be able to notice is that the page seems to load quicker!
Let’s be honest: the idea of inlining critical CSS and preloading the rest seems fairly straightforward if your project consists of a page or two. Just do a bit of investigation to see what classes are first visible on different device aspect ratios, inline those, and preload the rest.
But add hundreds of different pages and a whole team of developers all working on different parts and you can quickly see how keeping track of critical and non-critical styles can be cumbersome, if not impossible. We would be greatly helped by some tooling here.
Did you know that Chrome has a tool to find unused JavaScript and CSS code? This coverage feature is quite manual but can help you discover what styles on your page are unused when you first load the page and then scroll or click around.
There is a project called Penthouse that generates critical CSS for your webpages. The author of this tool also runs a paid service on top of Penthouse with automation and other features.
Addy Osmani has a tool called Critical that extracts and inlines critical-path CSS from HTML. He also maintains a list of other tools for critical CSS, which is well worth diving into.
Ultimately, tooling for such a task really depends on the framework, build process, and other libraries you are using to develop your site with. I’d encourage you to peruse Addy’s list of tools and see if any of them could work in your stack.
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowSimplify component interaction and dynamic theming in Vue 3 with defineExpose and for better control and flexibility.
Explore how to integrate TypeScript into a Node.js and Express application, leveraging ts-node, nodemon, and TypeScript path aliases.
es-toolkit is a lightweight, efficient JavaScript utility library, ideal as a modern Lodash alternative for smaller bundles.
The use cases for the ResizeObserver API may not be immediately obvious, so let’s take a look at a few practical examples.
17 Replies to "Improve site performance by inlining your CSS"
Except that’s not how it works. The very last element on the page could be styled position: fixed; top: 0; by a loaded stylesheet, which would place it at the top of the page, to be seen initially on first load. The browser cannot and does not render until all styles are available, because iclt cannot know this until that point At best, you break even placing the critical styles inline; at worst, you slow things down by transferring more data because you have those critical styles in your stylesheet as well.
Typical bad styling advice by a newbie JavaScript developer. I’ve heard it all before! The reason we have web standards is because someone already tried it this way in the 1990s and ran into nightmarish problems with keeping code clean and reusable.
Performance on the nanosecond scale is useless if your code looks like spaghetti bolognese and cannot be maintained!
If you’re going to give advice that goes against the current standards you should at least try to solve for all the reasons there is a standard in the first place!
This simple trick but very helpfull 😁
This laughably bad advice and it seems that the author has never worked on a on a large team project if he thinks that inline styles are practical.
Simply minifying the stylesheet is enough but the main thing is making the code maintainable and reusable.
Beware of Content Security Policy header!
We did a full circle. All these years being told not to use inline css for maintainability and all of the sudden it’s the latest trick for more performance.
It’s also good to inline all images using base64 encoding. Each image request also takes network time and as image files are much larger than CSS files, this speeds page load time :o;
all resourses – CSS, JS, images – are cached by browser… so they are loaded from server only first time page is loaded. Moving all that stuff as inline onto index significsntly increases traffic because they are downloaded literally with every request again and again… it’s BAD advice.
It’s a shame so few people know how to use css. I’ve seen people use a dozen elements to do what could be easily accomplished with a single properly styled element. So their css file becomes an unmaintainable mess and blows up. You do things correctly, your case file will be small and there will not be any issues.
So many of the problems in the modern computing age are just problems caused by doing something in a dumb way and then trying to fix the resulting mess by doing more things. It never occurs to people to look at why they ran into issues in the first place. Fix the problem, don’t implement a solution to fix the problem.
The next logical step from your conclusion would be to put your critical styles back into an external stylesheet on order to benefit from the long-term browser cache, in order to not have to load extra data whenever you load another page of the website (or the same page, since most html pages have a max-age of a few minutes, compared to the months to years given to stylesheets).
As the result, the true solution to the Above-the-Fold/Below-the-Fold issue is to use two css files, one being critical and loaded on the spot; the other being defered.
Some CSS preprocessors even include tools to help with this issues.
It is to note that there usually are a lot of things that can be optimized in a web page before going to such a length, including minifying resources (which this website doesn’t do), placing non-critical scripts at the bottom of the page (which this website doesn’t do), and NOT loading 50 different script files (Which this website does, half of them coming from this domain or wp.com)
Thanks for the comment, we’re aware of our performance deficiencies and are working to improve them. We’ll pass your feedback on to our lead developer.
I came to this post through my recent exposure to full page block editing pushed more aggressively by wordpress. I always believed minifying, consolidating, moving all the design and interaction stuff extern and the head is the road to performance victory. But I must come back from that.
1} content onthe web is short lived.
2} Network and devices got incredbly fast
3} not all webpages are the same
4) extensive engeneering costs time and money.
Did you notice this page to be slow, Aralcis? Or did you assume it most because of lack of minifying and other optimzed stuff? I didn’t notice a bit and reading this on an old android phone over an ok network.
As an age old sligtly ADHD developer I confess myself a convert. I used to take many hours squeezingout the last byte, carefully watching network turnarrounds and rendering times in browsers devtools only to realize they have little or no effect on the viewers experience.
As designer / developer we have to churn out designs at a fast pace fir a shortlived medium and for day to day sites this whole optimize thing made sense only in the dark ages of the web. Webapplications are a different thing btw.
I understand how external CSS style sheets can affect page performance, but there was a reason why CSS mostly became an external file. This entire article failed to address that important reason, which was to provide the logistic power to alter styling to a large number of posts or pages by changing one or a few CSS files.
That was one of the main reasons to place CSS into an external style sheet, so we would not have to manually change hundreds of pages/posts. This important element in styling web pages should never be ignored.
Use of inline CSS should not be manual, meaning inserted into a specific webpage, unless it is used on a very limited number of pages, because to change the inline CSS becomes a manual nightmare for a large number of pages. This is why website developers use external CSS files, PHP include functions, etc (especially to insert inline CSS styles on the fly), because they know the arduous suffering when having to alter or fix CSS entries on a large website, but then these methods do require network calls.
Don’t misunderstand the purpose of my post. I totally agree to the concept of critical CSS, which refers to only the CSS needed for the “above the fold” paint (loading) of a web page, but something needs to develop that addresses the requirement of large CSS files or many CSS files and page performance and speed. To date, all I see are spot fixes and nothing is resolving, in its entirety, the ongoing issue of page performance.
In addition, do not forget the server end of things. I currently moved my business website to a hosting service that is the best in the market, very fast SSD servers and the page speed of my site has increased dramatically. So for page performance there are a lot of parts to address to ensure you have the best performance possible, but remember, replacing external CSS files is not going to work using inline CSS.
Dumping STYLE into the markup remains one of the DUMBEST things you can do in HTML/CSS, and pissing and moaning lame excuses about “render time” rarely justifies the practice. If not for the one or two corner cases in which it makes sense — multi-page site with differing banners of the same style across pages for example — I would see it stricken from HTML entirely.
This idiocy about how much “faster” it is doesn’t hold any weight once you navigate to sub-pages, as putting it in the markup instead of external misses your chance to pre-cache! the more pages of like style you have, the faster and faster this derpitude blows up in your face.
I am SICK TO DEATH of going into client sites where this mind-numbingly dumbass practice of slopping style into the markup was attempted to “speed up a site” when it’s basically a band aid on a bullet wound.
MAYBE if people stopped using 90k of HTML to do 10k’s job, half a megabyte of CSS in a half dozen separate files to do 48k or less in one file’s job, and multiple megabytes of scripting spread out over dozens of files to do 64k or less in two file’s job, they wouldn’t be diving for dumbass broken trickery like this.
ESPECIALLY now that we can push.
And for the LOVE OF CHRISTMAS, futzing around sending a screen media sheet to PRINT (hurr-durrz) where it would likely conflict with or screw your print capabilities, and then using JavaScript to fix it destroying accessibility in the process? /FAIL/ at web development so epic, I lack the words in polite company to truly convey my opinion. And even there you’ve got one of those onevent attributes that have no business in any HTML written after around 2003 when we stopped giving a flying purple fish about Netscape 4.x!
But then even your cryptic two-letter classes in your example throw up huge warning flags that your approach to writing HTML is utterly banjaxed.
Hopefully not as incompetent as the skin for this site, but in all likelihood that’s the case. Endless pointless static scripting in the markup, endless pointless static style in the markup, gibberish to nonexistent semantics, tags and attributes that have no business in any HTML written after 1997… hardly a shock it’s vomiting up 149k of markup for 20k of paintext and zero content media, not even 32k of HTML’s job.
Also this is 2020, you can stop saying type=”text/css” on a STYLE tag.
The sheer ignorance, incompetence, and ineptutude shown in using HTML and CSS properly these days is why stupid trickery like crapping CSS into the markup with the STYLE tag is nothing more than a cheap trick that does more harm than good. It’s bailing wire and bubble gum fixes to try and make up at the end of the process flaws and bloat that shouldn’t even exist in the first place.
I’m sick of it. I’m sick of having to tell clients SUCKERED by this type of chazerie that to REALLY fix things their entire site needs to be thrown out and started over.
Thank you
How does react change this story? Now that react has to manage that extra data might the time to mount and render be impacted and in turn increase the overall page load time vs a style sheet?
Mr. Vernon is way ahead of the game with his suggestions versus the backlash here. I came to this website to view this ONE page. Over 23 stylesheets were downloaded into my space and over 6mb of God who knows what (it’s still going). I feel abused. Viewing this page should have transferred about 150kb, I didn’t order a boat. Some of these guy’s need to stop being emotional and start using their heads. I pray for more inline styling except for the obvious that should go in the .