Critical CSS and CSS Code Splitting: Faster First Paint

Critical CSS and CSS code splitting are performance techniques that help a page look ready sooner by delivering the most important styles first and delaying the rest. They are especially useful when a site has large stylesheets, many page-specific styles, or a slow first paint on mobile networks.

Quick answer: Critical CSS is the small set of styles needed to render the initial visible content, often inlined in the page head. CSS code splitting is the practice of separating CSS into smaller files so each page or route loads only what it needs.

Difficulty: Intermediate

Helpful to know first: You will understand this better if you know how the browser loads stylesheets, what render blocking means, and how a page can include more CSS than it immediately needs.

1. What Is Critical CSS and CSS Code Splitting?

Critical CSS is the smallest set of rules needed to style the content that appears in the first viewport. CSS code splitting is the broader delivery strategy of separating CSS into multiple files so pages, routes, or components load only the styles they actually need.

In practice, this means a homepage may receive a tiny inlined stylesheet for above-the-fold layout, while the rest of the site styles load afterward or in separate files.

2. Why Critical CSS and CSS Code Splitting Matter

CSS can block rendering. If the browser must wait for a large stylesheet before it paints the page, the user sees a blank screen longer than necessary. This is especially noticeable on slower networks and mobile devices.

These techniques matter because they improve important user-facing metrics such as first contentful paint and largest contentful paint. They can also reduce wasted downloads when a user visits only one page but would otherwise receive styles for many other pages too.

3. Core Idea and Delivery Pattern

The core idea is simple: identify the styles needed right away, deliver them first, and postpone everything else.

Minimal delivery pattern

A common pattern is to inline critical styles in the document head and load the full stylesheet in a non-blocking way. The browser can then paint the initial content quickly while the full CSS finishes loading.

<head>
  <style>
    /* Critical CSS: only the styles needed for the first view */
    body {
      font-family: Arial, sans-serif;
      margin: 0;
    }

    header {
      padding: 1rem;
      background: #123;
      color: #fff;
    }
  </style>

  <link rel="stylesheet" href="/css/app.css">
</head>

This example shows the idea, but in a real project the inlined block should stay small and focused on above-the-fold content.

How splitting works

Instead of shipping one large CSS file, you divide styles into smaller files by page, route, or component. For example, a product page may load product-specific styles while the checkout page loads checkout styles separately.

The browser still needs to parse CSS, but now it does less work on each page and transfers less data overall.

4. Step-by-Step Examples

Example 1: Critical CSS for a simple landing page

This example keeps only the first-screen styles in the head so the hero section can render immediately.

<head>
  <style>
    html, body {
      margin: 0;
      font-family: system-ui, sans-serif;
    }

    .hero {
      min-height: 100vh;
      display: grid;
      place-items: center;
      text-align: center;
    }
  </style>
  <link rel="stylesheet" href="/css/site.css">
</head>

This works because the hero can be styled before the external stylesheet finishes loading.

Example 2: Route-based CSS splitting

On a multi-page site, you can ship a base stylesheet plus a page-specific file for the current route.

<head>
  <link rel="stylesheet" href="/css/base.css">
  <link rel="stylesheet" href="/css/products.css">
</head>

If the user is on the products page, the browser does not need checkout styles yet. That keeps the first load smaller and easier to cache by page.

Example 3: Splitting shared and page-specific rules

Shared layout rules belong in a common file, while page-only rules belong in a separate file. That avoids duplicating the same selectors across multiple stylesheets.

/* base.css */
.container {
  max-width: 72rem;
  margin: 0 auto;
  padding: 0 1rem;
}

/* dashboard.css */
.chart {
  min-height: 20rem;
  background: #f5f7fa;
}

The shared container styles are loaded everywhere, but the chart styles only load on pages that actually contain charts.

Example 4: Non-critical CSS that can wait

Some styles are not needed for the first screen, such as footer decorations or secondary panels below the fold. Those can be delayed to reduce initial cost.

footer {
  border-top: 1px solid #ddd;
  padding: 2rem 1rem;
}

.newsletter-panel {
  margin-top: 4rem;
  background: #fafafa;
}

Because these rules are not required for the first viewport, they are good candidates for the non-critical bundle.

5. Practical Use Cases

6. Common Mistakes

Mistake 1: Inlining too much CSS

Critical CSS should be small. If you inline the entire stylesheet, you lose most of the performance benefit and make the HTML harder to cache efficiently.

Problem: This approach turns every page into a large HTML response and duplicates styles that could have been cached separately.

<style>
  /* Entire site stylesheet pasted into the page */
  .header { padding: 1rem; }
  .nav { display: flex; }
  .card { border: 1px solid #ddd; }
  .footer { padding: 2rem; }
  /* thousands of lines more */
</style>

Fix: Inline only the rules needed for above-the-fold content and keep the rest in external files.

<style>
  .header { padding: 1rem; }
  .hero { min-height: 60vh; }
</style>
<link rel="stylesheet" href="/css/site.css">

The corrected version improves first paint without throwing away caching benefits for the full stylesheet.

Mistake 2: Splitting CSS without a shared base

If every page gets its own isolated CSS file with duplicated resets and layout rules, the site becomes harder to maintain and may download the same declarations multiple times.

Problem: The browser may still fetch repeated styles, and future changes become error-prone because the same rules exist in several files.

/* home.css */
html, body { margin: 0; }
.container { max-width: 72rem; }

/* about.css */
html, body { margin: 0; }
.container { max-width: 72rem; }

Fix: Put shared rules in a base file and keep only page-specific styles in the split file.

/* base.css */
html, body { margin: 0; }
.container { max-width: 72rem; }

/* about.css */
.team-photo { border-radius: 1rem; }

This structure keeps shared styling consistent while preserving the benefits of smaller page-specific bundles.

Mistake 3: Loading non-critical CSS in a way that still blocks rendering

Some developers delay a stylesheet incorrectly and wonder why the page still feels slow. If the browser treats the stylesheet as render-blocking, the benefit disappears.

Problem: The stylesheet is still part of the blocking path, so the browser waits before painting the page.

<head>
  <link rel="stylesheet" href="/css/print.css">
</head>

Fix: Keep only the essential stylesheet in the critical path, and load the rest after the page can render.

<head>
  <style>
    .hero { min-height: 60vh; }
  </style>
  <link rel="stylesheet" href="/css/site.css">
</head>

The corrected version keeps the critical path short, which is the whole point of the technique.

7. Best Practices

Practice 1: Keep critical CSS small and viewport-focused

Only include styles that affect content the user can see immediately. The more CSS you inline, the more you increase HTML size and maintenance cost.

<style>
  .hero {
    display: grid;
    place-items: center;
  }
</style>

This is better than including every decorative rule on the page.

Practice 2: Separate shared styles from page-specific styles

A base stylesheet should hold shared layout, typography, and reset rules. Page bundles should only contain selectors unique to that page.

/* shared.css */
body { font-family: system-ui, sans-serif; }
.container { max-width: 72rem; }

/* checkout.css */
.payment-summary { background: #fff8e6; }

This reduces duplication and makes it easier to reason about what each page actually needs.

Practice 3: Load non-critical CSS as a normal stylesheet when it can wait

Not all CSS needs special treatment. If a stylesheet is not needed for initial rendering, keep it outside the critical block and let the browser load it after the essential styles are in place.

<head>
  <style>
    .hero { min-height: 50vh; }
  </style>
  <link rel="stylesheet" href="/css/secondary.css">
</head>

This keeps the page usable quickly while still delivering the rest of the design.

8. Limitations and Edge Cases

A common surprise is that a smaller stylesheet is not always faster if it causes too many extra requests or if the critical block is inaccurate. Performance is about the full delivery path, not file size alone.

9. Practical Mini Project

Here is a small example for a marketing page that uses a minimal critical block plus a separate stylesheet for the rest of the page.

<head>
  <style>
    body {
      margin: 0;
      font-family: system-ui, sans-serif;
    }

    .hero {
      min-height: 70vh;
      display: grid;
      place-items: center;
      text-align: center;
    }
  </style>
  <link rel="stylesheet" href="/css/marketing.css">
</head>
<body>
  <main class="hero">
    <div>
      <h1>Launch faster</h1>
      <p>Deliver the first screen quickly, then load the rest of the design.</p>
    </div>
  </main>
</body>

This mini project shows the main pattern: the hero area is styled immediately, while additional page styling can arrive from the external file.

10. Key Points

11. Practice Exercise

Try this exercise to check your understanding of critical CSS and CSS code splitting.

Expected output: The header and hero should appear styled immediately, while lower-page decoration can load later without affecting the first screen.

Hint: Ask yourself which elements are visible without scrolling. Those are usually the best candidates for the critical block.

<head>
  <style>
    body {
      margin: 0;
      font-family: system-ui, sans-serif;
    }

    header {
      padding: 1rem;
      background: #0f172a;
      color: #fff;
    }

    .hero {
      padding: 4rem 1rem;
      text-align: center;
    }
  </style>
  <link rel="stylesheet" href="/css/footer.css">
</head>
<body>
  <header>Acme Studio</header>
  <main class="hero">
    <h1>Design faster pages</h1>
    <p>The first screen should feel complete right away.</p>
  </main>
</body>

12. Final Summary

Critical CSS helps the browser paint the first screen as quickly as possible by giving it only the styles it needs right away. CSS code splitting broadens that idea by dividing styles into smaller, page-aware pieces so each page downloads less CSS and does less work.

Used well, these techniques improve perceived speed, reduce render blocking, and keep large sites easier to scale. Used poorly, they can create duplication, extra complexity, or too many separate files.

If you want to go further, next learn how the browser loads stylesheets, how render blocking affects page timing, and how to audit unused CSS so you can decide what belongs in the critical path.