For years, I wished for a magical open-source tool that would make building Custom Elements feel as easy and enjoyable as crafting components in Vue, Svelte, or React.
But that tool never showed up.
We’ve had options like Lit and Skate that work—but let’s be honest: they are dated and clunky.
Furthermore, why must a library require build steps and extensive boilerplate? What frontend developer actually wants to use a class-based syntax, or frigging decorators?
And, don’t even get me started on the pains of wrestling Tailwind CSS or other global CSS into the Shadow DOM—it’s like trying to fold origami with oven mitts.
What I really wanted was something:
- With strong type auto-inference
- Functional, not class-based
- Free of dependencies
- Stupidly simple to use
- Tailwind-style utility classes and animations baked-in, at runtime
- Friendly to SSR, routing, and inter-component communication (e.g., event bus & global store)
- And, of course—CDN-ready so I could just drop it in without bundlers
So I stopped waiting and I built it. 💥
Meet my Custom Elements Runtime—a lightweight, no-frills tool that blends the best parts of Vue, React, Svelte, Tailwind CSS, and Web Components into one tasty package.
Define a Component
import { component, ref, html, useProps, useEmit } from '@jasonshimmy/custom-elements-runtime';
component('my-counter', () => {
const props = useProps({ initialCount: 0 })
const emit = useEmit();
const count = ref(props.initialCount);
const handleClick = () => {
count.value++;
emit('update:initial-count', { count: count.value });
};
return html`
<button
type="button"
class="px-4 py-2 bg-blue-500 text-white rounded"
@click.prevent="${handleClick}"
>
Count: ${count.value}
</button>
`;
});
Use it in HTML
<my-counter
initial-count="5"
></my-counter>
<script>
const counterEl = document.querySelector('my-counter');
counterEl.addEventListener('update:initial-count', (event) => {
console.log('Count updated to:', event.detail.count);
})
</script>
✨ Why You’ll Love It
- Blazing Fast: Minimal runtime, instant updates, zero dependencies.
- JIT CSS: Utility-first styling on demand, directly in your HTML.
- Built-in Animations: Vue-style Transition and TransitionGroup functions with presets.
- No Build Required: Just write and run—no bundling overhead.
- TypeScript First: Strong types, IntelliSense, and safety built in.
- Functional API: No classes, no boilerplate—just functions.
- SSR & HMR Ready: Universal rendering + instant hot reloads.
- Extensible: Directives, routing, event bus, store, and more.
- Developer Friendly: Clean docs, sharp examples, and it "just works."
It’s fast, flexible, powerful, genuinely fun to develop with, and is truly a one-stop shop with everything you would ever need.
It is The Complete Web Components Framework.
Try it out on Codepen and drop a ⭐ on GitHub.
While you're at it, play a fun little game I developed, Solatro, which is simplified version of the popular card-based game, Balatro (original credit for the Solatro tabletop game goes to TechDweeb). And, yes, I built the entire game with the Custom Elements Runtime.
Have an awesome day.
Top comments (0)