Why Web Components?
Web components are a powerful way to build reusable and encapsulated components and they provide several great benefits that make them stand out in the ever-expanding ecosystem of UI development tools:
- Encapsulation: logic and styles are contained within the component itself, keeping behavior and appearance consistent.
- Framework-agnostic: web components integrate easily with any framework or work independently, ensuring flexibility and long-term use.
- Uniformity: reusable components help maintain a consistent look and feel across applications.
With those benefits in mind, Lit takes web component development a step further by offering tools to streamline and optimize coding workflow. Lit simplifies the creation and management of web components, offering a robust set of features to enhance the development workflow.
This post will cover the foundational concepts of Lit, from defining custom elements and handling properties to lifecycle methods, event management, and styling. If you’re eager to dive deeper, check out the Lit workshop we held during the Frontend Community of Practice (FE COP), where we covered these topics in detail. During the workshop, we explained all the concepts and demonstrated how to build web components using Lit.
Lit Workshop Videos and repository
Lit workshop part 1
This introduction to Lit and web components explores the basics: defining custom elements, managing attributes and properties, rendering, working with slots, handling internal state, and using Lit directives.
Lit workshop part 2
Part two dives into more advanced topics, such as styling components, working with lifecycle methods, handling events, managing signals and context APIs, and integrating Lit components with popular frameworks.
Code examples from the workshop
The code examples from the workshop are available on GitHub: mds-workshops (lit-playground), so you can follow along at your own pace.
Summary of key concepts in Lit and best practices
Defining custom elements
Custom elements in Lit are straightforward to define and use. Here’s what you need to know:
- Custom element definition: define custom elements using the global
customElementsregistry. - Class-based components: create components as classes for better organization and reusability.
Properties and attributes
Lit simplifies handling data and its flow in components by distinguishing between properties and attributes:
- Property declaration: use decorators to define properties with initial values and types.
- Attributes vs. properties: attributes handle simple values, while properties support complex data types.
- Attribute reflection: reflect properties as attributes for styling or DOM manipulation (e.g. toggling disabled or loading states).
- Custom attribute names: align property names with JavaScript conventions while exposing intuitive attribute names in HTML.
Render method
Lit’s render method is at the heart of defining templates. Whenever state or properties change, this method handles re-rendering:
- Template definition: define the component’s template using the render method.
- Template composition: use separate functions to compose the template for better readability and maintainability.
- Changing property values: avoid changing the value of your properties or states in the render method. For that, use other lifecycle methods, like:
willUpdate.
Working with slots
Slots make content insertion seamless in web components:
- Default slots: useful for simple, content insertion.
- Named slots: allow more targeted content placement by assigning names to slots.
- Accessing slot content in the component: you can use Lit decorators like:
@queryAssignedElementsto access the content passed into slots. - Styling slotted content from the component: use the
::slottedpseudo-element to style slotted elements from within the shadow DOM.
Managing internal state
Lit provides tools for managing internal component state efficiently. Key points to consider include:
- State management: use Lit’s
@statedecorator to use internal state. - Rendering: setting the state values will trigger re-rendering.
- Lifecycle methods: use lifecycle methods like
willUpdateto manage state changes.
Leveraging Lit directives
Lit provides powerful directives to help to control rendering logic. Here are some of the most commonly used directives:
ifDefined: this directive is used for conditional rendering. It ensures that an element or attribute is only rendered if a certain condition is met. This is particularly useful for handling optional attributes or elements that should only appear under specific circumstances.when: similar to ifDefined, the when directive is used for conditional rendering but offers more flexibility. It allows you to specify a condition and two templates: one for when the condition is true and another for when it is false. This makes it easier to manage different states within your component.repeat: this directive is used for iterating over arrays or lists of data. It helps in rendering a list of items efficiently by reusing DOM elements where possible. This is particularly useful for rendering dynamic lists or tables.until: is used for asynchronous rendering. It allows you to display intermediate states, such as loading indicators, while waiting for asynchronous data to be fetched. This enhances the user experience by providing visual feedback during data loading.
Styling components
Because of the shadow DOM, styles defined inside a component cannot be overridden from the outside. This ensures consistency and prevents accidental style overrides. Here are some key points to consider:
- Styling the component: inside the component, you can style elements using regular CSS. For example, you can define a paragraph with a specific class and style it accordingly.
- Class map directive: this directive allows you to dynamically add classes to elements based on component properties. This is useful for applying different styles based on the state or attributes of the component.
- Reflected properties: they are attributes that can be styled from both inside and outside the component. For example, if a property is set to disabled, it can be reflected as an attribute and styled using CSS.
- Styling content within slot: it’s possible to style slotted content from the component using the
::slottedpseudo-element. Elements within slots can be styled from outside the shadow DOM, giving users control over the appearance of slotted content.
Component lifecycle management in Lit
There are multiple lifecycle methods available in Lit that allow you to control behavior at each stage of your component’s existence:
connectedCallback: this method is called when the component is added to the DOM. It’s a good place to set up any initial state or perform tasks that need to happen when the component is first rendered, like fetching data or setting upwindowordocumentevent listeners.disconnectedCallback: the method is called when the component is removed from the DOM. It’s useful for cleaning up any resources likewindowordocumentevent listeners that were set up in connectedCallback. You don’t need to clean-up any other local event listeners or properties, as they will be garbage collected automatically.attributeChangedCallback: this method is called when an attribute of the component is added, removed, or changed. It’s useful for reacting to changes in the component’s attributes and updating the component’s state accordingly.render: this method is used to define the component’s template. It’s called whenever the component’s state changes and needs to be re-rendered.firstUpdated: the method is called after the component’s template has been rendered for the first time. It’s a good place to perform any setup that depends on the component’s DOM being fully rendered.willUpdate: this method is called just before the component is rendered. It’s useful for performing tasks that need to happen before the component’s property value or state is updated, like validating state, doing some calculations or acting upon the state or property change.updated: the method is called whenever the component’s properties change and the component is re-rendered. It’s useful for performing tasks that need to happen after the component has been updated.
Handling events
Events are a crucial part of web components, allowing components to connect with the “outside” world and communicate changes. These changes typically occur due to user interaction. For example, a button dispatches a click event when a user clicks on it, an input dispatches a input event when the user enters a value in it.
You can dispatch a regular events or custom events using the dispatchEvent method. Read more about custom events, events and dispatching events.
Conclusion
Lit simplifies creating modern, efficient web components with a robust set of tools. Its intuitive APIs make managing properties, styling, event handling, and lifecycle behavior seamless. Whether you’re starting fresh or integrating into existing frameworks, Lit empowers developers to build reusable, accessible, and future-proof components with ease. Dive into the workshops, experiment with the examples, and let Lit transform how you approach web development.