
Modern frontend development has a problem.
We keep rebuilding the same UI components — Selects, Toasts, Modals —
over and over again, just because the framework changed.
React today.
Vue tomorrow.
Maybe Qwik, Solid, or something else next year.
And every time, the UI stack resets.
I decided to stop playing that game.
This post is about why I built framework-agnostic UI components using native Web Components,
and why I believe this approach is not a niche experiment — but the future of UI primitives.
The Real Problem with Modern UI Libraries
Most UI libraries today are:
- ❌ Locked to a single framework
- ❌ Heavy on dependencies
- ❌ Painful to migrate away from
- ❌ Fragile in SSR environments
- ❌ Inconsistent in accessibility
Even “headless” libraries are still framework-bound.
The result?
UI logic becomes disposable every time your stack changes.
That’s unacceptable for long-lived products.
My Goal: UI That Outlives Frameworks
I wanted UI components that:
- Work everywhere
- Have zero framework lock-in
- Are small, fast, and predictable
- Support real-world features, not demos
- Treat accessibility and SSR as defaults, not add-ons
The answer was obvious:
Native Web Components.
Why Web Components (And Not Another Abstraction)
Web Components are:
- A browser standard
- Framework-agnostic by design
- Compatible with every modern UI stack
- Stable over time
No virtual DOM magic.
No render lifecycle traps.
No dependency graph explosions.
Just elements, events, and state.
What I Built
I started with the two components that cause the most pain in real projects:
- SeoSelect — a production-grade Select component
- SeoToast — a universal Toast notification engine
Both are built as pure Web Components, then wrapped for frameworks optionally.
- SeoSelect → https://www.npmjs.com/package/seo-select
- SeoToast → https://www.npmjs.com/package/seo-toast
SeoSelect: Not “Another Select Library”
Select components are deceptively complex.
Real apps need:
- Large datasets
- Fast rendering
- Search (multilingual, fuzzy)
- Keyboard navigation
- Accessibility
- Custom styling
- Type safety
So I built SeoSelect as a real Select engine, not a demo widget.
Key Features
- Zero dependencies (pure Web Component)
- Works with React, Vue, Angular, Solid, Qwik, Vanilla
- Virtual scrolling for large datasets
- Fuzzy multilingual search (Korean initial consonants, Japanese romaji, Chinese pinyin)
- Full keyboard & screen reader support
- CSS Variables for theming
- Typed events with global TypeScript support
Comparison: SeoSelect vs Popular Select Libraries
| Feature | SeoSelect | react-select | Headless UI | antd / Prime |
|---|---|---|---|---|
| Framework-agnostic | ✅ | ❌ | ❌ | ❌ |
| Vanilla JS support | ✅ | ❌ | ❌ | ❌ |
| Zero dependencies | ✅ | ❌ | △ | ❌ |
| Virtual scrolling | ✅ | ❌ | ❌ | △ |
| Multilingual fuzzy search | ✅ | ❌ | ❌ | ❌ |
| Accessibility by default | ✅ | △ | △ | △ |
| SSR-safe | ✅ | ❌ | △ | ❌ |
| Typed native events | ✅ | ❌ | ❌ | ❌ |
| CSS variable theming | ✅ | △ | △ | ❌ |
SeoSelect is not “good for a Web Component”.
It’s simply better for long-term products.
SeoToast: A Universal Toast Engine
Toast notifications shouldn’t require a framework.
They should just work.
SeoToast is designed to be:
- Small
- Predictable
- Accessible
- SSR-safe
- Framework-independent
Key Features
- Multiple types, positions, and animations
- Duplicate message grouping
- Progress bar with hover pause
- ARIA & keyboard accessibility
- ~10KB gzipped
- Optional wrappers for major frameworks
Comparison: SeoToast vs Popular Toast Libraries
| Feature | SeoToast | react-toastify | sonner | notistack |
|---|---|---|---|---|
| Framework-agnostic | ✅ | ❌ | ❌ | ❌ |
| Vanilla JS support | ✅ | ❌ | ❌ | ❌ |
| SSR-safe | ✅ | ❌ | △ | △ |
| Duplicate grouping | ✅ | ❌ | ❌ | ❌ |
| Zero dependencies | ✅ | ❌ | ❌ | ❌ |
| Accessibility | ✅ | △ | △ | △ |
Why This Approach Matters
Frameworks come and go.
Your UI behavior should not.
With Web Components:
- One UI engine
- Every framework
- Same UX
- Same events
- Same types
- Same accessibility guarantees
This isn’t about rejecting frameworks.
It’s about refusing to let them own your UI logic.
This Is Not an Experiment
I’m not “trying” Web Components.
I’m committing to them.
Because UI primitives should be:
- Stable
- Reusable
- Portable
- Boring (in the best way)
If this direction resonates with you, feel free to explore:
- SeoSelect → https://www.npmjs.com/package/seo-select
- SeoToast → https://www.npmjs.com/package/seo-toast
Feedback, criticism, and real-world usage stories are more than welcome.
Let’s build UI that outlives frameworks.
Top comments (4)
Really enjoyed this — it resonated because it mirrors a lot of the reasons I’ve been spending time in the Web Components space lately.
I’m currently authoring Glint, an ongoing experiment exploring whether there’s a good middle ground between what the platform already gives us (custom elements, shadow DOM, etc.) and the declarative DX we’ve collectively learned not to hate from frameworks like React.
It’s early, evolving, and very much a work in progress — but your post articulates exactly the tension that motivated it.
glintjs.vercel.app
Thanks a lot for sharing Glint — I’ve been following similar questions from the Web Components side as well.
The way you’re exploring a middle ground between the platform primitives and a more declarative DX really resonates with me. That tension is exactly what pushed me to start experimenting in this space too.
Excited to see how Glint evolves — this feels like the kind of exploration the ecosystem genuinely needs.
Really love the clever cartoon toaster in the quick demo of
seo-toast-- great job!Thanks! Glad you liked the little toaster 😄
I wanted the demo to feel simple and friendly, so that means a lot.