6 min read

Are Inline Styles Faster than CSS?

by Daniel Nagy @danielnagydotme
🧬Written by a human
Old people racing on electric scooters

I recently migrated my website from CSS-in-JS to inline styles and discovered that inline styles improved the load performance of my website. Based on this discovery, I wrote a blog post suggesting that switching to inline styles could improve a website's performance.

When I shared my post on internet forums, people correctly pointed out that migrating from a specific CSS-in-JS library to inline styles was too ambiguous to suggest that inline styles could be faster than CSS.

Many people were convinced that CSS was faster than inline styles. In an effort to seek the truth, I decided to conduct a less ambiguous and more conclusive experiment.

To perform this experiment, I migrated my website to CSS. Yes, I migrated my entire website to CSS, and yes, it was incredibly tedious.

I used BEM for styling my components, and I used atomic CSS for styling elements that were not explicit components. I ended up with a little more than 2,000 lines of handwritten CSS.

I built three versions of my React app. The first version was the control using inline styles.

<div style="color: blue;" />

The second version used a CSS file.

<link rel="stylesheet" href="index-27557ee9.css" />

And for the third version, I inlined the CSS in a style tag in the head of the document. I will refer to this as inline CSS.

<style>
/* CSS here */
</style>

I deployed all three versions to a single preview environment for testing. I could switch between the different versions using a query parameter in the URL. I then took measurements of the following:

  • Server render time
  • HTML size
  • JavaScript bundle size
  • Browser render time
  • Web vitals

This is an n-of-1 experiment, but my website is more complex than a todo app and more realistic than a synthetic benchmark. Here are the results.

Server Render Time#

I render my React application on the server and send the pre-rendered HTML to the browser. I measured the time it took to render my application on the server for my home page and for my blog post about deploying a React app to Vercel.

I selected my home page because it is often the first page my users see. In addition, I selected my blog post about deploying a React app to Vercel because it is my longest blog post to date and, therefore, has the most HTML (over 6,000 nodes). For consistency, I conducted all my experiments using these two pages.

Here is some pseudocode for how I measured the time it took to render my application.

const start = performance.now();
await render(<App />);
const end = performance.now();
const total = end - start;

I calculated the average render time for each page, and here are the results.

Render Time
Time (ms)
0
75
150
225
300
Inline Styles
CSS
148 ms
146 ms
Measurements taken on a Fly.io micro-vm with 1 core of shared CPU and 1GB of memory (lower is better)

The time it took to render my React app was very inconsistent. However, when I took the average, the times were nearly identical.

The reason for the inconsistency is likely IO. I make some amount of API requests on each page, which is inherently variable in time.

However, there was no measurable correlation between inline styles and the time it took to render my application on the server. It would appear that the time it takes to stringify my inline styles is insignificant.

I'll admit that, since IO is happening, this test is not conclusive. It would be better to eliminate all variables. However, for static websites, the rendered HTML could be cached, and, in that case, the time it takes to render doesn't matter as much.

HTML Size#

Here is a size comparison of the HTML generated by each version of my app.

HTML Size
Size (kB)
0
75
150
225
300
Total
Gzip
79 kB
42 kB
74.7 kB
10.9 kB
7.7 kB
14.7 kB
Inline Styles
CSS
Inline CSS
(lower is better)

As you might expect, inline styles produced the largest HTML documents. However, after compression, the difference wasn't very significant.

What's interesting is that after compression, inline styles produced smaller documents than inline CSS. I believe this is because there is more repetition of inline styles, making them ideal for compression.

JavaScript Bundle Size#

Here is a size comparison of the JavaScript generated by each version of my app.

JavaScript Bundle Size
Size (kB)
150
300
450
600
750
Total
Brotli
696 kB
683 kB
264 kB
263 kB
Inline Styles
CSS
(lower is better)

Inline styles did increase the size of my JavaScript bundle. But again, after compression, the difference was insignificant. Remarkably, styling my entire website using inline styles only increased my JavaScript bundle by 1 kB after compression!

I want to point out that the size of my CSS file, minified and brotli'd, is 8.9 kB. That means inline styles actually resulted in the fewest bytes over the wire! At least for the first page visit before anything is cached in the browser.

I do not currently code-split my JavaScript, but if I did, then inline styles would naturally get chunked up as well.

Browser Render Time#

Now let's have a look at browser render performance for each version. For these tests, I allowed the browser to cache the CSS file.

Inline Styles
FP
CSS
FP
Inline CSS
FP
0Time (ms)85
Parse
Style
Layout
Paint
Measurements taken in Chrome v123.0.6312.87 developer tools for 576 nodes

Parse, style, layout, and paint are stages of the browser's rendering pipeline. It is how it turns HTML into pixels.

The chart shows how long it took the browser to render each page once it started parsing the HTML. It also shows the frequency and time spent in each stage of the rendering pipeline.

While inline styles wasn't always the fastest to render the entire page, it was consistently the fastest to put pixels on the screen. This can be seen by looking for the first paint indicator (FP) in the chart.

There are a couple interesting observations from this data. The first observation is that for small pages, like my home page, a fast computer may parse the HTML before it parses the CSS (even from the cache).

If you look closely at the CSS timeline for my home page you'll see a second blue bar. That is the browser parsing the CSS. It can't render anything until it parses the CSS because CSS is render blocking.

The other interesting observation is that the browser (at least Chrome) appears to parse HTML for roughly 10 milliseconds before attempting to ship a frame. For inline CSS, the browser may need to process multiple chunks of HTML before it finishes parsing the CSS.

Furthermore, both CSS and inline CSS appear to tax first paint. This is especially noticeable on low powered devices. This makes sense, if you give the browser a wall of CSS, it has to cut through all that CSS before it can render anything.

Web Vitals#

To round things out let's compare web vitals for each version of my app. To measure web vitals on my own devices I used the web-vitals npm package maintained by Google Chrome.

For these tests, I was connected to my home wifi (360 Mbps) and I allowed the browser to cache the CSS file.

Web Vitals
Time (ms)
0
200
400
600
800
1000
1200
1400
FCP
LCP
FID
INP
600.73 ms
650.61 ms
728.71 ms
600.73 ms
650.61 ms
728.71 ms
19.38 ms
13.11 ms
14.63 ms
35 ms
52 ms
49.14 ms
Inline Styles
CSS
Inline CSS
Measurements taken in Chrome v123.0.6312.87 using web-vitals v3.5.2 on a 2019 MacBook Pro (lower is better)

More often than not, inline styles outperformed CSS when it came to first contentful paint (FCP) and largest contentful paint (LCP). This is consistent with the data for render performance.

There wasn't a measurable correlation between inline styles and first input delay (FID) or interaction to next paint (INP). Both inline styles and CSS were well below the 200 millisecond threshold to feel instantaneous.

Now let's take a look at Lighthouse and PageSpeed Insights to see if the trend continues.

Web Vitals
Time (s)
0
2
4
6
8
10
FCP
LCP
TBT
SI
0.33 s
0.65 s
0.4 s
0.4 s
1.18 s
0.53 s
0 s
0.03 s
0 s
0.88 s
0.93 s
0.88 s
Inline Styles
CSS
Inline CSS
(lower is better)

There appears to be a slight advantage to to inline styles and inline CSS compared to CSS on desktop. However, on mobile there is a significant advantage to inline styles and inline CSS compared to CSS.

Both Lighthouse and PageSpeed Insights show horrible performance for CSS on mobile. This is inconsistent with the data I collected from my own devices. I believe this is because, when they conduct their tests, the browser's cache is empty and the network speed is throttled.

If you think about it, the first time a user visits a website the browser's cache will be empty. This means the first experience a user has with a website will be the worst in terms of performance.

Conclusion#

There was no measurable correlation between inline styles and the time it took to render my React application on the server.

In addition, while inline styles did increase the size of my HTML and JavaScript, the difference was insignificant after compression. In fact, inline styles resulted in the fewest bytes total for the browser to download, at least on first page visit.

Furthermore, using inline styles the browser is able to start painting pixels earlier in the rendering process. This is especially true on mobile or low powered devices.

Over time, CSS will increase in size as more features are added to a website. This will negatively impact performance of existing pages.

Based on the data, I believe there is evidence to suggest that inline styles are better for performance than CSS. However, this may not be true for every website. I encourage people to do their own experiments and seek their own truth. The more data we collect the more we will understand.

Written by Daniel Nagy

Daniel is a software engineer and full-stack web developer. He studied computer science at Ohio University and has been doing web development and hybrid mobile app development since 2014.

If you liked this post, then please consider donating or becoming a sponsor. Your support will help me produce more content that gives back to the community.

Comments#

INFO

You will not receive notifications for new comments. If you are waiting for a reply, please check back periodically.

Alexey Lyakhovcommented on Apr 13, 2024

The main problem with inline css is happens later, then you wish to setup strict Content Security Policy, and you understand that you have to set unsafe-inline, which is not recommended. Or you have to rewrite all your inline styles as css

Daniel Nagy • Authorcommented on Apr 13, 2024

@Alexey Lyakhov That is a good callout that you have to use style-src 'unsafe-inline', if you have a CSP, for inline styles to work.

It's my understanding that inline styles only pose a security risk if you have dynamic inline styles from user input or if your website, or one of its dependencies, has been compromised.

Do you know if static inline styles are a security risk? I don't believe they are.

Wout Mertenscommented on Apr 14, 2024

Ok so I totally understand if you don't want to do this, but I suspect that using tailwind actually will result in less css overall, while being a lot like online styles, and will thus perform best.

It would be really enlightening to see this research amended with a tailwind version of the pages.

Daniel Nagy • Authorcommented on Apr 14, 2024

@Wout Mertens I agree that tailwind would likely scale better, in terms of size, than other methods of authoring CSS.

When it comes to load performance, however, I don't believe that it would perform best. Any CSS is a potential tax on first paint, and tailwind isn't exempt from that fact.

When it comes to repainting, tailwind may perform better than inline styles. It is theoretically less work for the browser to parse the class attribute than it is for it to parse the style attribute. It is unknown if this is significant or not. My blog post mainly focuses on load performance.

Shirkitcommented on May 9, 2024

I'm surprised to see the huge difference in the mobile results. Maybe there's something else at play here? 6x difference is a suspiciously big diffence.

Daniel Nagy • Authorcommented on May 9, 2024

@shirkit Those differences were measured using Lighthouse and PageSpeed Insights. I can't pretend that I know exactly how those tools are conducting those tests.

When I conducted my own tests using the web-vitals package, the difference wasn't as extreme. I did allow the browser to cache the CSS, and I was connected to my home wifi, though. Which likely accounts for some of the difference.

Aditya Mishracommented on May 9, 2024 • Edited

You missed a very crucial detail, by separating styles in a separate .css file, you can re-use that file, what that means is on initial page load the .css file is loaded, but on next page loads, the file doesn't need to be refetched as browser will cache it, thus reducing your load time and even FCP & LCP stuff.

Daniel Nagy • Authorcommented on May 9, 2024

@Aditya Mishra I did not miss that. In my tests, I allowed the browser to cache the CSS file.

Keep in mind that the first time a user visits your website, nothing will be cached. A user's first experience with your website is the worst in terms of performance.

Hassan Abu-Jabircommented on May 10, 2024

Maybe there could be a general rule of thumb abstracted from this measurement. The next intersting experiment would be to test what happens if we only inline some of the CSS. Maybe having all render blocking CSS as inline CSS and then loading everything else is the sweetspot. Intuitively speaking this should remove the second render step completely.

Jordan Hoffmancommented on May 26, 2024 • Edited

Thank you for this detailed analysis. To me (or perhaps my company), literally the only thing that matters is the lighthouse score to improve SEO. And the LCP on mobile is always a score destroyer at my company. It was shocking to see how horrible a tax they give to external css. Using inline styles will dramatically improve the score. The only problem with inline styles is you can't do media queries or things like hover selectors for them. But I guess you could use inline css for that which also gets a good score.

Markdown enabled