Before writing a single line of code with Lit, it is worth understanding what problem it comes to solve. Lit is not a framework that invents its own component model from scratch, the way React or Vue do: it is a lightweight layer built directly on top of a web platform standard called Web Components. Understanding that standard first — what it offers and what it lacks — is the best way to understand why Lit exists and what a developer gains by using it. This lesson lays the theoretical foundations of the course and introduces the project you will build throughout the ten modules: TaskFlow, a Kanban-style task management application.
Contents
- The problem: interface components without a common standard
- Web Components: the three technologies that make up the standard
- What "vanilla" Web Components are missing
- What Lit brings to the table
- Lit versus frameworks like React, Vue or Angular (a first look)
- The course's guiding project: TaskFlow
- The problem: interface components without a common standard
For years, every JavaScript framework has solved the same problem — splitting an interface into reusable, encapsulated pieces with their own state — in its own way. React has its components with JSX, Vue has its .vue files, Angular has its components with decorators. All of them are excellent solutions, but they share a drawback: a React component cannot be used directly inside a Vue application without adapter layers, and vice versa. Every framework defines its own concept of "component," and that concept does not cross its own borders.
This raises several practical problems:
- Portability: if a company builds a library of interface components (buttons, cards, forms) with a specific framework, teams using a different framework cannot reuse it without friction.
- Longevity: frameworks evolve and sometimes become obsolete. A component tied to a specific version of a framework can stop working once that framework is abandoned.
- Learning cost: every framework imposes its own way of thinking about components, even though the problem they solve is conceptually the same in every case.
The natural question is: why doesn't the browser itself offer a native mechanism for creating reusable interface components, without depending on any framework? That question is precisely what Web Components answer.
- Web Components: the three technologies that make up the standard
"Web Components" is not a single API, but the name given to three W3C specifications that, combined, allow you to create custom HTML elements with their own behavior. It is worth knowing each one separately before seeing how Lit uses them.
Custom Elements
The Custom Elements specification allows you to define new HTML tags, with a name chosen by the developer (always containing a hyphen, like <task-card> or <user-avatar>, to avoid collisions with the browser's native tags). Once defined, that tag behaves like any other: it can be inserted into the HTML, it has its own lifecycle, and the browser recognizes it as a real DOM element, not a simulation.
class SaludoElemento extends HTMLElement {
connectedCallback() {
this.textContent = '¡Hola desde un Custom Element!';
}
}
customElements.define('mi-saludo', SaludoElemento);With just these lines, <mi-saludo> is already a valid HTML tag that any modern browser understands, with no need for any framework or prior compilation.
Shadow DOM
Shadow DOM solves the encapsulation problem. When a component has its own internal HTML and its own CSS styles, it is easy for those styles to "leak" and affect the rest of the page, or for external styles to break the component's appearance. Shadow DOM creates a separate DOM tree (a "shadow") hanging off an element, with its own CSS that does not leak beyond its boundaries and that also does not receive external styles by accident.
class Caja extends HTMLElement {
connectedCallback() {
const raiz = this.attachShadow({ mode: 'open' });
raiz.innerHTML = `
<style>
div { border: 2px solid teal; padding: 8px; }
</style>
<div>Contenido encapsulado</div>
`;
}
}
customElements.define('mi-caja', Caja);How Lit works with styles and Shadow DOM in detail is studied in depth in the styles module; for now it is enough to know that it exists and what it is for: isolating a component from the rest of the page.
HTML Templates
The <template> tag lets you declare fragments of HTML that the browser parses but does not render until JavaScript code decides to clone and insert them. It is the foundation on which many efficient templating solutions have historically been built, because the content of a template can be cloned many times without parsing it again.
These three pieces — Custom Elements, Shadow DOM and HTML Templates — are standards natively supported by all modern browsers. They do not depend on any library.
- What "vanilla" Web Components are missing
If the standard already solves the problem, why isn't it enough to use it directly, without any library? In practice, working with Custom Elements "bare-handed" turns out to be tedious and error-prone, for several reasons:
| Common need | How it is solved with vanilla Web Components |
|---|---|
| Updating the interface when a piece of data changes | You have to manually locate the affected DOM nodes and modify them by hand (textContent, setAttribute...) |
| Avoiding rebuilding the entire HTML on every change | You have to write comparison and selective DOM update logic by hand |
| Declaring the template in a readable way | It is common to fall back on concatenating HTML strings, with the risk of errors and security issues (HTML injection) |
| Reacting when several properties change at once | You have to manually program the batching of updates so as not to repaint too much |
| Writing less repetitive code | There is no convenience layer: every component repeats similar patterns |
None of this is impossible to solve by hand, but it means writing and maintaining a lot of "plumbing" code that adds no business value. The more components an application has, the more this cost is felt.
- What Lit brings to the table
Lit is a lightweight library created by Google (the heir to an earlier project called Polymer) whose sole purpose is to make writing Web Components pleasant, without hiding or replacing the underlying standard. A component written with Lit is a real Custom Element; Lit does not create a parallel component model, it simply adds a layer of syntactic sugar and optimizations on top of the native API. Its main contributions are:
- Declarative reactive templates: you write the component's HTML using a tagged template literal function (
html), in a very readable style, and Lit takes care of updating only the parts of the DOM that actually change. - Less repetitive code: declaring reactive properties, reacting to their changes and re-rendering is reduced to a few lines, compared to the manual management the native DOM would require.
- Performance: Lit does not re-parse or rebuild the entire HTML on every update. Internally it identifies which template expressions have changed and updates only those points of the DOM, which makes it very efficient even in interfaces with many frequent updates.
- Small size: Lit weighs just a few compressed kilobytes, well below the typical size of a full application framework.
- Standard compatibility: since it generates real Custom Elements, a Lit component can be used on any HTML page, with any framework, or with none at all.
In short, Lit does not compete with the Web Components standard, it embraces it: it takes the most uncomfortable parts of working with Custom Elements and Shadow DOM directly and turns them into a comfortable, modern development experience.
- Lit versus frameworks like React, Vue or Angular (a first look)
It is common to wonder how Lit fits in compared to the big application frameworks. The short answer is that they are not exactly comparable: React, Vue and Angular are full application frameworks, with their own component model, routing, global state management and ecosystem; Lit is a library focused exclusively on creating components that are, in addition, native browser standards.
This has an important practical consequence: a component made with Lit can be used inside a React, Vue or Angular application as if it were just another HTML tag, and it can also be used on a page without any framework at all. A React component, on the other hand, can normally only be used inside a React application.
This comparison will be revisited in much more detail — including concrete interoperability examples — in the integration and interoperability module. For now, it is enough to keep the idea that Lit and the big frameworks do not compete on the same ground: Lit is meant for building reusable, standard components; application frameworks are meant for building complete applications (and, in fact, they can use Lit components inside them).
- The course's guiding project: TaskFlow
Throughout this course you will not see loose, disconnected examples. Instead, everything that gets explained will be applied progressively to a single project: TaskFlow, a Kanban-style task management application (task columns like "To do," "In progress" and "Done").
TaskFlow will be built, module by module, as a set of Web Components made with Lit, including:
<task-card>: represents an individual card with the information for a task.<task-list>: a board column that groups several cards.<task-board>: the complete board, which organizes several lists.<task-filter>: a control for filtering the visible tasks.<user-avatar>: a small reusable component that shows the avatar of the person assigned to a task.
Each module of the course will add capabilities to these components: first they will be created statically, then they will be given state and reactivity, later carefully crafted styles, communication between them via events, lifecycle behaviors, advanced templating features, and finally, in module 10, everything will be integrated into a complete TaskFlow application, tested and ready to deploy.
Common Mistakes and Tips
- Confusing "Web Components" with "Lit": they are different things. Web Components is the platform standard; Lit is a library that makes it easier to work with that standard. You can use Web Components without Lit, but you cannot use Lit without the result being Web Components.
- Thinking that Lit is "just another framework" competing with React: as you have seen, its purpose is different (creating reusable standard components, not complete applications), although in practice it can be used to build entire applications, as will be done with TaskFlow in this course.
- Underestimating the value of the standard: anyone who already knows an application framework may feel tempted to skip this theoretical foundation. However, understanding which parts are "browser standard" and which are "Lit sugar" will help a great deal with debugging problems and reading technical documentation later on.
- Expecting Lit syntax in this lesson's example code: the native code examples (
HTMLElement,customElements.define,attachShadow) are intentionally "vanilla," without Lit, to show the pure standard. Lit's own syntax begins in lesson 3 of this same module.
Exercises
- Without using any library, create in an HTML file a Custom Element called
<mensaje-bienvenida>that, when inserted into the page, shows the text "Bienvenido a TaskFlow" inside a<p>. - Modify the previous exercise so that the text renders inside a Shadow DOM, with a CSS style that sets the background color to a light tone and stays encapsulated (that is, it does not affect the rest of the page).
- Write, in your own words and without looking at the lesson, a list of three "plumbing" tasks you would have to code by hand if you built
<task-card>with vanilla Web Components, without any library like Lit.
Solutions
<!DOCTYPE html>
<html lang="es">
<body>
<mensaje-bienvenida></mensaje-bienvenida>
<script>
class MensajeBienvenida extends HTMLElement {
connectedCallback() {
const parrafo = document.createElement('p');
parrafo.textContent = 'Bienvenido a TaskFlow';
this.appendChild(parrafo);
}
}
customElements.define('mensaje-bienvenida', MensajeBienvenida);
</script>
</body>
</html><!DOCTYPE html>
<html lang="es">
<body>
<mensaje-bienvenida></mensaje-bienvenida>
<script>
class MensajeBienvenida extends HTMLElement {
connectedCallback() {
const raiz = this.attachShadow({ mode: 'open' });
raiz.innerHTML = `
<style>
p {
background-color: #eef6ff;
padding: 8px;
border-radius: 4px;
}
</style>
<p>Bienvenido a TaskFlow</p>
`;
}
}
customElements.define('mensaje-bienvenida', MensajeBienvenida);
</script>
</body>
</html>By using attachShadow, the internal <style> only affects the <p> that is inside that same Shadow DOM tree: if the page had another <p> outside the component, it would not be affected by this style, and vice versa.
- One possible answer (the three most common tasks): (a) manually locating and updating the DOM nodes every time the task's data changes (title, status, assigned person); (b) writing by hand the logic so as not to rebuild the whole card from scratch on every change, but only update what is necessary; (c) manually managing the element's registration, its initial attributes, and the synchronization between HTML attributes and JavaScript properties.
Conclusion
In this lesson you have seen that Web Components are a native browser standard made up of three pieces — Custom Elements, Shadow DOM and HTML Templates — that allow you to create reusable, encapsulated HTML elements without depending on any framework. You have also seen that working with that standard directly turns out to be tedious, and that is exactly where Lit comes in: a lightweight library that adds reactive templates, efficient DOM updates and much less code repetition, while still producing standard Custom Elements. Finally, TaskFlow was introduced, the task management project we will be building throughout the whole course.
In the next lesson we will set theory aside and prepare the development environment: we will install Node.js, create a project with Vite, install the lit package, and get everything ready to write the first real component in lesson 3.
Lit Course
Module 1: Introduction to Lit and Web Components
- What are Web Components and why Lit?
- Setting Up the Development Environment
- Your First Lit Component
- Anatomy of a Lit Component
Module 2: Reactive Templates and Rendering
- Lit's Template Engine
- Expressions and Interpolation in Templates
- Conditional Rendering
- List Rendering
- The Rendering Cycle
Module 3: Reactive Properties and State
- Reactive Properties
- Internal State with @state
- Types of Properties and Custom Converters
- Attributes vs Properties and Reflection
Module 4: Styling Lit Components
- Encapsulated CSS with Shadow DOM
- Shared Styles Between Components
- Custom CSS Properties and Theming
- Slots and Styling Distributed Content
Module 5: Events and Component Communication
- Handling DOM Events in Templates
- Custom Events: Communication from Child to Parent
- Communication from Parent to Child with Properties
- Communication Patterns Between Sibling Components
Module 6: Lifecycle and Advanced Behavior
- Lifecycle Callbacks
- Reactive Hooks: willUpdate, updated, and firstUpdated
- Reactive Controllers
- Mixins and Composing Behavior
Module 7: Directives and Advanced Template Features
- Built-in Directives: classMap, styleMap and ifDefined
- Custom Directives
- Asynchronous Rendering with until
- Shared Context with @lit/context
Module 8: Integration, Interoperability and Deployment
- Using Lit Components in Plain HTML
- Integrating Lit with React, Vue, and Angular
- Server-Side Rendering with @lit-labs/ssr
- Bundling, Publishing, and TypeScript
Module 9: Testing and Best Practices
- Unit Tests with Web Test Runner
- Accessibility in Web Components
- Performance and Optimization
- Common Patterns and Anti-patterns
