DEV Community

Cover image for I Know This Will Upset Some Devs, but Tailwind + Shadcn/ui + Shadow DOM = Pain

I Know This Will Upset Some Devs, but Tailwind + Shadcn/ui + Shadow DOM = Pain

ujja on February 05, 2026

This recent post Is Learning CSS a Waste of Time in 2026? (by @sylwia-lask) really hit me, especially the part about accessibility dragging you st...
Collapse
 
sylwia-lask profile image
Sylwia Laskowska

Fair point - and honestly, thanks for writing this up so clearly. Real-world lessons like this are super valuable. Shadow DOM + Tailwind + portal-based UI libs sounds great on paper, but the practical tradeoffs are very real.

I’m glad you shared the pitfalls openly. Hopefully a lot of people read this before falling into the same trap and spending days debugging styling ghosts 😄

Collapse
 
ujja profile image
ujja • Edited

Thanks really appreciate you saying that 🙏 Shadow DOM Tailwind and shadcn/ui sounds great, but in reality you end up lost and in pain. 😅
Glad the post helps others avoid bundle bloat portal issues and all the little CSS headaches I ran into

Collapse
 
ujja profile image
ujja

Also I think the real lesson here is that Shadow DOM Tailwind and shadcn/ui all have their strengths, but they do not always play nicely together. Sometimes you just have to compromise. Use Shadow DOM only for simple components, let portals work normally, and accept a bit of global CSS to make things actually work in the real world.

Collapse
 
ben profile image
Ben Halpern

I like tailwind... and generally agree with all this

Collapse
 
ujja profile image
ujja

Thanks Ben. Someone has to call it out 🥲

Collapse
 
webreflection profile image
Andrea Giammarchi

imho, ShadowDOM is overrated and indeed often on the way ... you can ignore it and use either polyfills for native builtins or just tiny libraries that give you 100% class based Custom Elements powers without requiring you to use or need ShadowDOM at all and you can extend both HTML or SVG if you like and use CSS the way you like, those are just regular, good old, nodes ... nothing more, nothing less 👋

Collapse
 
ujja profile image
ujja

Yeah, honestly, I am starting to land in a similar place. Shadow DOM sounds powerful, but in practice, it often feels like more ceremony than value unless you really need that isolation. Once you control the app or design system end-to-end, plain old DOM plus good conventions go a long way. The moment you add utility CSS, portals, theming, or real-world UI libraries, Shadow DOM starts feeling like it is fighting you instead of helping. I think the hype came from a very valid problem space, but it does not fit nearly as many use cases as people assume.

Collapse
 
webreflection profile image
Andrea Giammarchi

Most importantly, Shadow DOM requires mandatory JS so that JS becomes easily a point of failures, while to show your layout as meant, no point of JS failure should be considered ... I'm making that suggested library as robust and helpful as anyone can wish for, and so far it delivers ... one tweak left on the style-able [is="my-element"] and nobody would ever complain after that, because Shadow DOM tries to solves UI elsewhere, and it's awkward on that, if you handle your whole project you wouldn't get a single advantage out of Shadow DOM ... the rule of thumbs is simple: do you provide components meant to be shown as Ads? Yes? Shadow DOM ... No? Don't even bother with that!

Thread Thread
 
ujja profile image
ujja

Fair take. The dependency on JavaScript for something as basic as visual correctness is an easy thing to underestimate until it bites you. When rendering depends on everything booting perfectly, the blast radius of even small failures gets much bigger than it needs to be.
I also like the practical framing you’re pointing at. There are scenarios where strong isolation makes sense, but when you fully own the surface area of the UI, the benefits get thin very quickly. In those cases, simpler primitives tend to be more robust, easier to reason about, and kinder to long term maintenance than adding another hard boundary to work around

Collapse
 
matheus_releaserun profile image
Matheus

yeah this is a real pain point. ran into the exact same issue building web components that needed to coexist with a tailwind app. the shadow boundary just murders any utility-class approach because the styles literally can't cross it.

we ended up adopting a hybrid — CSS custom properties for theming (those DO pierce shadow DOM) and scoped CSS inside the components. not pretty but it works.

imo the fundamental issue is that tailwind was designed for a world where everything lives in one DOM tree. shadow DOM breaks that assumption completely. it's not a tailwind bug, it's just a fundamental mismatch in mental models.

Collapse
 
ujja profile image
ujja

Yeah, I’ve come to a similar conclusion after fighting with this for a while. Once you put that boundary in place, a lot of the “it just works” ergonomics disappear, and you suddenly have to rethink how styles even get into a component.
Using variables for theme values and keeping the rest scoped locally feels like one of the few approaches that stays sane, even if it is a bit clunky. And I agree, this is less about any tool being broken and more about different assumptions colliding. Tailwind and Shadow DOM are both good at what they were designed for, just not in the same mental model

Collapse
 
ujja profile image
ujja

Also, you will not believe it, our team literally has a Confluence page dedicated to shadcn rants. We keep a running list of all the weird edge cases and issues we keep hitting. The funniest part is the client still really wants it, probably because it feels cooler or more modern. At this point it is half documentation, half therapy 😅

Collapse
 
leob profile image
leob • Edited

Excuse my ignorance, but do we really need ShadowDOM - isn't the solution simply not to use it? Is "name clashing" the only problem it solves, or does it do more?

Collapse
 
ujja profile image
ujja

Yeah you do not always need Shadow DOM, but it does solve more than just name clashing. The main reason to use it is encapsulation. It keeps a component’s internal markup and styles completely separate from the rest of the page and vice versa, so you avoid unexpected style or script conflicts when a component is reused in different places. That makes components more predictable and easier to reason about.
The tradeoff is that global CSS and utility frameworks like Tailwind do not automatically apply inside Shadow DOM, and things like portals can break because they render outside that boundary. So if your project does not really need strict isolation, for example if you control all the CSS or are not building a shared widget library, then skipping Shadow DOM is a totally reasonable choice.

Collapse
 
leob profile image
leob

Thanks, that makes sense!

Collapse
 
sephyi profile image
Sephyi

At least in my world, I considered ShadowDOM only during the development of browser extensions. Outside of that context, I didn’t have any specific use cases for it. I understand the objective here, but if I’m not mistaken, there’s a much more elegant way to achieve component isolation. However, I’ll need to look it up because I’m not sure what it is or if I’m just imagining things.

Collapse
 
ujja profile image
ujja

That matches my experience too. Browser extensions are probably the most convincing use case for Shadow DOM. You are injecting UI into pages you do not control, so isolation actually matters there. Outside of that, especially in app codebases, the cost often outweighs the benefit. There are other ways to get isolation that feel less heavy-handed, like CSS Modules, scoped styles, or just tighter component boundaries. Shadow DOM is great at what it does, but most apps do not actually need that level of isolation to be successful.