js

Why Lit and Shoelace Are the Future of Framework-Agnostic Web Components

Discover how combining Lit and Shoelace enables fast, reusable, and framework-independent UI development using native web components.

Why Lit and Shoelace Are the Future of Framework-Agnostic Web Components

I’ve been thinking about web components a lot lately. Not as a passing trend, but as a fundamental shift in how we build for the web. The promise of truly reusable, framework-agnostic UI pieces is compelling, but the raw Web Components API can feel verbose. That’s why I started looking at two tools that, when combined, create a remarkably smooth development experience: Lit and Shoelace.

Let me explain why this pairing works so well. Lit is a tiny library from Google that acts as a friendly helper for creating web components. It gives you a simple, declarative way to define templates and manage state, without the overhead of a larger framework. You write less boilerplate and focus more on your component’s logic. Shoelace, on the other hand, is like a well-stocked toolbox. It’s a collection of beautiful, accessible, and ready-to-use web components—things like buttons, modals, dropdowns, and date pickers. They look great out of the box but are built to be customized.

So, what happens when you use them together? You get the best of both worlds: the ability to quickly drop in high-quality UI elements from Shoelace and the power to build your own bespoke components with Lit. All of it speaks the same language—the native language of the web platform.

Think about the last time you needed a complex input field with validation, or a modal dialog. With Shoelace, you can have it in minutes. Here’s how simple it is to use one of its components. First, you install the library and load a component.

npm install @shoelace-style/shoelace

Then, in your HTML or JavaScript, you import and use it. No framework setup required.

<script type="module">
  import '@shoelace-style/shoelace/dist/components/button/button.js';
</script>

<sl-button variant="primary">Click Me</sl-button>

But what if Shoelace’s button is almost right, but you need it to do something special? This is where Lit shines. You can create a new component that uses or extends Shoelace’s foundation. Let’s say you need a button that tracks how many times it’s been clicked.

import { LitElement, html } from 'lit';
import '@shoelace-style/shoelace/dist/components/button/button.js';

class ClickCounterButton extends LitElement {
  static properties = {
    count: { type: Number },
  };

  constructor() {
    super();
    this.count = 0;
  }

  _increment() {
    this.count++;
  }

  render() {
    return html`
      <sl-button variant="primary" @click=${this._increment}>
        Clicks: ${this.count}
      </sl-button>
    `;
  }
}
customElements.define('click-counter-button', ClickCounterButton);

See what we did there? We created a new custom element, <click-counter-button>, that internally uses the Shoelace button. We added reactive state (count) and logic with Lit’s clean syntax. This component can now be used anywhere, in any project, regardless of the main framework.

This approach solves a major headache for teams. How often have you built a design system in React, only to need it for a Vue project later? The effort to port or wrap components is significant. With Lit and Shoelace, you build with web standards. Your components are native HTML elements. They work in React, Vue, Angular, or with no framework at all. You maintain one codebase for your UI library, not three or four.

The performance story is also worth noting. Because these are true web components, they leverage the browser’s built-in capabilities. There’s no large framework runtime to download and parse. Your users get interactive pages faster. Have you considered how much framework code your users download for simple interactive widgets?

Let’s build something a bit more practical. Imagine a task list item that uses Shoelace’s beautiful checkbox and card components, enhanced with Lit’s reactivity.

import { LitElement, html } from 'lit';
import '@shoelace-style/shoelace/dist/components/card/card.js';
import '@shoelace-style/shoelace/dist/components/checkbox/checkbox.js';

class TaskItem extends LitElement {
  static properties = {
    taskTitle: { type: String },
    isComplete: { type: Boolean },
  };

  constructor() {
    super();
    this.taskTitle = 'New Task';
    this.isComplete = false;
  }

  _toggleCompletion() {
    this.isComplete = !this.isComplete;
  }

  render() {
    return html`
      <sl-card>
        <div slot="header">
          <sl-checkbox
            ?checked=${this.isComplete}
            @sl-change=${this._toggleCompletion}
          >
            ${this.taskTitle}
          </sl-checkbox>
        </div>
        <div>Status: ${this.isComplete ? 'Done' : 'Pending'}</div>
      </sl-card>
    `;
  }
}
customElements.define('task-item', TaskItem);

This TaskItem component is self-contained, stylish, and functional. The ?checked=${this.isComplete} syntax is Lit’s way of binding a boolean attribute. The @sl-change listener responds to Shoelace’s checkbox change event. It’s a clean integration.

Getting started is straightforward. Create a new project, install both libraries, and begin. You can use a tool like Vite or Webpack, or even load them directly via CDN for prototyping. The barrier to entry is low, but the ceiling for what you can build is very high.

What kind of application could you build with this stack? I’ve seen it used for internal dashboards, design system foundations, marketing sites that need lightweight interactivity, and even complex single-page applications. The key is that you’re not locked into a specific ecosystem. Your investment is in the web platform itself.

I find this combination liberating. It allows me to think about UI as discrete, reusable pieces without worrying about framework compatibility. I can prototype quickly with Shoelace’s pre-built components and then seamlessly switch to crafting my own with Lit when the need arises. The development feedback loop is fast, and the final output is lean and interoperable.

If you’re tired of framework churn or maintaining multiple component libraries, this path offers a stable alternative. It’s about building on a standard that will outlast any particular library’s popularity. Give it a try. Start by adding a Shoelace button to a plain HTML file, then try wrapping it in a simple Lit element. You might be surprised by how much you can accomplish.

I’d love to hear what you build with this approach. Have you tried combining web component libraries before? What was your experience? If you found this perspective useful, please share it with a colleague or leave a comment below. Let’s continue the conversation about building a more resilient web.


As a best-selling author, I invite you to explore my books on Amazon. Don’t forget to follow me on Medium and show your support. Thank you! Your support means the world!


101 Books

101 Books is an AI-driven publishing company co-founded by author Aarav Joshi. By leveraging advanced AI technology, we keep our publishing costs incredibly low—some books are priced as low as $4—making quality knowledge accessible to everyone.

Check out our book Golang Clean Code available on Amazon.

Stay tuned for updates and exciting news. When shopping for books, search for Aarav Joshi to find more of our titles. Use the provided link to enjoy special discounts!


📘 Checkout my latest ebook for free on my channel!
Be sure to like, share, comment, and subscribe to the channel!


Our Creations

Be sure to check out our creations:

Investor Central | Investor Central Spanish | Investor Central German | Smart Living | Epochs & Echoes | Puzzling Mysteries | Hindutva | Elite Dev | JS Schools


We are on Medium

Tech Koala Insights | Epochs & Echoes World | Investor Central Medium | Puzzling Mysteries Medium | Science & Epochs Medium | Modern Hindutva

Keywords: web components,lit,shoelace,framework agnostic,ui development



Similar Posts
Blog Image
Why Bun and Elysia Are Changing How We Build Fast, Type-Safe APIs

Discover how Bun and Elysia deliver blazing-fast performance and end-to-end type safety for modern API development.

Blog Image
Build High-Performance GraphQL API with NestJS, Prisma, and Redis Caching - Complete Tutorial

Build high-performance GraphQL API with NestJS, Prisma, and Redis. Learn DataLoader patterns, caching strategies, authentication, and real-time subscriptions. Complete tutorial inside.

Blog Image
Building Resilient Systems with Event-Driven Architecture and RabbitMQ

Learn how to decouple services using RabbitMQ and event-driven design to build scalable, fault-tolerant applications.

Blog Image
Build a Type-Safe GraphQL API with NestJS, Prisma and Code-First Schema Generation Tutorial

Learn to build a type-safe GraphQL API using NestJS, Prisma & code-first schema generation. Complete guide with authentication, testing & deployment.

Blog Image
Complete Guide to Next.js Prisma Integration: Build Type-Safe Full-Stack Apps in 2024

Learn how to integrate Next.js with Prisma ORM for type-safe, full-stack web applications. Build efficient database-driven apps with seamless data flow.

Blog Image
How to Build Type-Safe GraphQL APIs with TypeORM and TypeGraphQL

Unify your backend by using TypeScript classes as both GraphQL types and database models. Learn how to simplify and scale your API.