DEV Community

Cover image for The Modern 2025 Web Components Tech Stack
Matsuuu
Matsuuu

Posted on

3

The Modern 2025 Web Components Tech Stack

In the modern day it's easy to find a tech stack as it usually is just a quick npm create something and you get a full scaffolded development setup. The same doesn't really happen in the Web Components space though. With Web Components, it's more like a all-you-can-eat buffet. You pick what you want and you go all in on it.

That's what makes it really hard especially for starting developers to find the correct toolchain to get started with developing with Web Components and being productive with them.

I'm here today to showcase my current go-to development stack and how I like to use my tools. I've used this stack in multiple production-grade setups and will continue to do so with slight variations.

In this post we'll be going through the following technologies:

So let's get started!


Lit

Lit markets itself as a

Lit is a simple library for building fast, lightweight web components.

And that is simply all it delivers. Lit is built from 3 parts:

  • Lit HTML, which handles dynamic rendering in a Javascript context
  • Lit Element, which provides an easy interface for creating reactive Web Components
  • Helper functions, which make everything feel like a framework

Lit has been my go-to since the old Polymer days and it's still my daily driver for modern web frontends. It gets the job done and allows you to keep productive in pretty much any environment.

With Lit, it's as simple as

const name = "World";

html`Hello ${name}!`;
Enter fullscreen mode Exit fullscreen mode

Suunta

This is an opinionated post, and this is an opinionated statement: Suunta is one of the best ways to handle views and state in Web Component environments.

With Suunta, you can manage SPA applications easily, while also being able to create reactive views, without having everything be a component.

What makes Suunta versatile, is that it doesn't ship a renderer, but allows the user to implement it themselves. The "sane default" here is to use Lit as the renderer, and it's just a matter of adding a one-liner function to your codebase.

Suunta provides the ease-of-use of a framework with the footprint of a helper library.

import { html } from "lit";
import { createState } from "suunta";

export function HomeView() {
    const state = createState({
        clicks: 0,
    });

    function onClick() {
        state.clicks += 1;
    }

    return () => html` <button @click=${onClick}>Clicked ${state.clicks} times</button> `;
}

Enter fullscreen mode Exit fullscreen mode

Suunta provides routing, state management and sane helpers for things like API calls. Combined with Lit, you are pretty much all set to write any modern web application.

Tailwind

Whether you like it or not, Tailwind is making the rounds. I've started adopting Tailwind to my dev setups due to co-worker pressure but it's starting to grow on me a bit.

As my current default dev setups include Vite as the default dev server / build tool, implementing a Tailwind setup is as easy as it can be.

After installing tailwindcss and @tailwindcss/vite, all you have to do is setup your Vite to handle tailwind, and import it into your css.

// vite.config.ts
import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  server: {
    port: 8000,
  },
  plugins: [tailwindcss()],
});
Enter fullscreen mode Exit fullscreen mode
/* main.css */
@import "tailwindcss";
Enter fullscreen mode Exit fullscreen mode

And voilá! You have a functional Tailwind setup for your regular views.

But what about web components and shadow root?

For those pesky shadow roots and encapsulations, we have to do some extra work to have tailwind pierce through them.

Luckily, it can be achieved with some modern Typescript in just a few lines of code.

Adding this helper into your project will allow you to inject Tailwind into any component inside of your project.

import { css, LitElement, unsafeCSS } from "lit";
import mainStyles from "./main.css?inline";

export type LitElementConstructor<T = typeof LitElement> = T & {
    new (...args: any[]): LitElement;
};

export function withTailwind(constructor: LitElementConstructor) {
    const styleInject = css`
        ${unsafeCSS(mainStyles)}
    `;

    // Append to existing array if set
    if (Array.isArray(constructor.styles)) {
        constructor.styles.push(styleInject);
        return;
    }

    // If not value at all, init an array with styles
    if (!constructor.styles) {
        constructor.styles = [styleInject];
        return;
    }

    // If value is set, but is singular instead of array, make it an array.
    constructor.styles = [constructor.styles, styleInject];
}
Enter fullscreen mode Exit fullscreen mode

After writing this helper function, it's just the matter of adding the decorator to your component class.

@customElement("my-button")
@withTailwind
export class MyButton extends LitElement {
    render() {
        return html`
            <button
                class="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded"
            >
                Click me
            </button>

        `;
    }
}
Enter fullscreen mode Exit fullscreen mode

And you're all setup! You have...

  • Rendering in Javascript-land
  • Re-usable components via Lit
  • Routing and state management through Suunta
  • Easy helper-class style css via Tailwind

And all without having any framework magic under the surface, just simple libraries working perfectly together.

Closing

I hope this setup provides you with a clear starter for your next web development project. I've also lefta repo with the same setup pre-setup if you want to clone that and start fiddling. https://github.com/Matsuuu/modern-tech

AWS Q Developer image

Your AI Code Assistant

Generate and update README files, create data-flow diagrams, and keep your project fully documented. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

Top comments (0)

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay