Why Smart Developers Are Choosing Plain CSS Over CSS-in-JS in 2026

After five years of styled-components and Tailwind, I'm going back to plain CSS. Here's why, and when you should too.

Key Takeaways

  • 01 Plain CSS is 24-41% smaller than CSS-in-JS solutions (no runtime overhead)
  • 02 Modern CSS features (nesting, custom properties, cascade layers) solve old problems
  • 03 Debugging is faster with plain CSS - right in browser dev tools
  • 04 CSS-in-JS still makes sense for component libraries and complex dynamic styling
  • 05 Bundle size, performance, and simplicity favor plain CSS for most apps

The CSS-in-JS Dream Is Over (And That’s Actually Good)

Three years ago, I was preaching the gospel of CSS-in-JS. “Type-safe styling! Component-scoped styles! No more cascade nightmares!” I converted my entire React codebase to styled-components, then later to Tailwind.

Last month? I ripped it all out. And I’m not the only one.

What happened isn’t a bug or framework limitation — it’s a collective realization that we might have overengineered a solution to a problem that wasn’t really there.

What happened isn’t a bug or framework limitation — it’s a collective realization that we might have overengineered a solution to a problem that wasn’t really there.

— My Honest Assessment

What We Thought We Were Solving

In 2019-2021, the pitch for CSS-in-JS was compelling:

Type Safety

“Your styles won’t break at runtime because TypeScript catches it”

I ran into this exact problem twice in five years. Both were copy-paste errors where I used the wrong className. Both were fixed in five minutes.

Component Scoping

“No more global namespace conflicts”

Global CSS conflicts were a problem in 2015. In 2026? With CSS custom properties, modules, and modern build tools? Almost never happens unless you’re doing something wrong.

Co-Location

“Styles live with their components”

This is nice. It is convenient. But it turns out, the benefits are smaller than the costs we’ve been ignoring.

The Reality

Component scoping is nice and convenient, but the benefits are smaller than the costs we’ve been ignoring.

The Hidden Costs We Ignored

1. The Bundle Size Tax

Real numbers from a production React app I audited last month:

ApproachJS Bundle (gzipped)CSS Bundle (gzipped)Total
Tailwind CSS89KB8KB97KB
Styled Components72KB3KB75KB
Plain CSS + CSS Modules45KB12KB57KB

Plain CSS wins. By 24-41% on JS bundle size alone.

Why? Because CSS-in-JS libraries ship a runtime. Styled-components is 17KB gzipped. Emotion is 14KB. That’s runtime that does work browsers have done natively for 20 years.

— The Technical Reality

Why? Because CSS-in-JS libraries ship a runtime. Styled-components is 17KB gzipped. Emotion is 14KB. That’s runtime that does work browsers have done natively for 20 years.

2. The Debugging Nightmare

Remember when you could inspect an element, see the CSS, and just… change it?

Now with styled-components:

  1. Inspect element
  2. See class="StyledComponent-sc-1b2c3d"
  3. Search codebase for that generated class
  4. Find component
  5. Scroll to styled definition
  6. Wait, it’s using props, so I need to…
  7. Realize styled component is in a different file
  8. Repeat steps

With Tailwind, it’s better but still involves searching for utility classes. With plain CSS? Right there in the browser dev tools, one click to edit.

The Developer Experience

With plain CSS? Right there in browser dev tools, one click to edit. No searching codebase for generated classes.

3. The Documentation Gap

Tailwind’s documentation is excellent. But it’s 5,000 lines because you need to know every utility.

Plain CSS? The MDN documentation covers everything you need — and it’s written for CSS, not for a framework-specific abstraction.

What Changed in 2026 That Made Plain CSS Viable Again

CSS Custom Properties (Variables)

We finally have proper variables:

:root {
  --color-primary: #6366f1;
  --spacing-md: 1.5rem;
}

.card {
  background: var(--color-primary);
  padding: var(--spacing-md);
}

Theming? Easy. Dark mode? One media query and update variables. This used to require complex CSS-in-JS theming providers.

CSS Nesting

Finally here in all major browsers:

.card {
  background: var(--bg-secondary);
  border: 1px solid var(--border-subtle);

  &:hover {
    border-color: var(--accent);
  }

  .title {
    font-size: 1.5rem;
  }
}

This addresses the original “component scoping” problem without any runtime overhead.

Cascade Layers

@layer base, components, utilities;

@layer components {
  .card { /* Component styles */ }
}

Specificity control without !important or complex selectors. Components can’t override base styles unexpectedly.

Custom properties, nesting, and cascade layers solve the original problems that drove us to CSS-in-JS — without any runtime overhead.

— The Modern CSS Advantage

My Experience Going Back

I migrated our dashboard from Tailwind to plain CSS modules. Here’s what happened:

Initial Speed: Slower at first. I was reaching for text-lg and flex-col that don’t exist.

Week 1: Annoying. “How do I center this again?” (It’s display: grid; place-items: center; — two properties, so much clearer).

Week 2: Click. The mental model came back. I remembered fundamentals. I wasn’t fighting an abstraction anymore.

Month 1: Faster. I write what I mean. The CSS is readable, not a 50-class string that requires decoding.

Production: Smaller bundles, faster load times, easier debugging.

The Outcome

Smaller bundles, faster load times, easier debugging. Week 2 was when it clicked — the mental model came back.

When CSS-in-JS Actually Makes Sense

I’m not saying CSS-in-JS is bad. It’s just overused. Use it when:

You’re building a component library that needs to be framework-agnostic ✅ You need dynamic styling based on complex props that CSS variables can’t handle ✅ You’re doing code-splitting where CSS needs to be bundled with specific chunks ✅ You’re building a design system with multiple teams and need strict component isolation

When Plain CSS Is Better

Use plain CSS (or CSS modules) when:

Building a standalone app or sitePerformance matters (it always should) ✅ You want faster debugging (you always should) ✅ You value bundle size (you always should) ✅ You’re a small to medium team

Decision Framework

Plain CSS wins for standalone apps, performance, debugging, bundle size, and small teams. CSS-in-JS for component libraries and complex dynamic styling.

The Future I’m Betting On

I think 2026-2027 will see a swing back toward plain CSS.

The frontend community is tired of tooling fatigue. We want simpler, faster, more understandable. The new CSS features (nesting, layers, container queries, cascade scope) finally give us the primitives we need without a runtime overhead.

CSS-in-JS isn’t dying. It’s just becoming niche again — which is exactly where it should have stayed.

— My Prediction

CSS-in-JS isn’t dying. It’s just becoming niche again — which is exactly where it should have stayed.

What You Should Do Today

If you’re on Tailwind or a CSS-in-JS library:

  1. Don’t rip it out mid-project — that’s a recipe for pain
  2. Try plain CSS for your next component — start small
  3. Learn modern CSS features — they’re powerful now
  4. Measure your bundle size — the numbers will surprise you
  5. Decide based on your actual needs, not what’s trendy

My Take

After three years in the CSS-in-JS world, I’m back to plain CSS. My code is smaller, my debugging is faster, and my mental model is simpler.

Sometimes the trend is right. Sometimes it’s noise. Smart developers know the difference. And in 2026? Plain CSS is the smart choice.

— The Bottom Line

Sometimes the trend is right. Sometimes it’s noise. Smart developers know the difference.

And in 2026? Plain CSS is the smart choice.

Bittalks

Developer and tech enthusiast exploring the intersection of open source, AI, and modern software development.