- Lab
- Core Tech

Guided: Web Components Framework Integration
Modern apps demand flexible UI solutions. In this Code Lab, you’ll refactor a React task manager to integrate Web Components — bridging framework boundaries for reusable, scalable UIs. You'll tackle real-world challenges: syncing state bidirectionally, handling component lifecycles, and avoiding memory leaks.

Path Info
Table of Contents
-
Challenge
Introduction
Welcome to the lab Guided: Web Components Framework Integration.
In this Code Lab, you will assume the role of a software developer tasked with integrating a Web Component into an existing Task Manager application that has been built with React. Through the course of this lab you will learn how to:
- Integrate a Web Component into a React component.
- Use HTML attributes to pass string data to Web Components.
- Pass complex data structures from a React component to a Web Component.
- Pass data from a Web Component to a React component.
Task Manager Application
The application that you'll be working with includes the following files:
index.html
: The entrypoint for the app, which loads the the React application JavaScript.src/main.jsx
: Handles rendering of the React application.src/index.css
: Basic styles for the application.src/App.jsx
: A task manager component implemented with React. It implements functionality for creating, completing and deleting tasks.src/App.css
: Styles for the ReactApp
component.src/task-item.js
: A Web Component that renders the UI for a single task: a checkbox, the text for the task, a delete button. This component is not yet integrated into the React application.
The application has been configured with the Vite build tool. This provide React transpilation and a development server.
How to Work On Tasks
The tasks in this lab will instruct you to make changes in the
App.jsx
andtask-item.js
files. These files are already open and ready for you to work with in the code editor.While you are working on a task you can preview your changes to the application. On the Terminal tab, click the Run button. Once the development server has started, switch to the Web Browser tab and click the Refresh button on the left to load the Task Manager app.
Whenever you make changes in
App.jsx
ortask-item.js
you will need to stop the development server in the Terminal by pressingCtrl + C
on your keyboard, then click the Run button again, switch to the Web Browser tab and click the Refresh button.Checking Your Work
To check your work for a task, click the Validate button above the task steps. This will run tests against your code and let you know if there's anything that you need to change.
The solutions for each task can be found in the
solution
directory in the Filetree. -
Challenge
Integrate a Web Component
Component Lifecycles
The lifecycle of React components is state centric, whereas the lifecycle of Web Components is Document Object Model (DOM) centric. When integrating Web Components with React components these differences manifest in the following ways:
1. Core Lifecycle Triggers
Web Components use native browser lifecycle callbacks, for example:
connectedCallback
when the component is mounted,disconnectedCallback
for unmount,attributeChangedCallback
for attribute updates. These callbacks are triggered by DOM mutations. React components, however, rely on a virtual DOM and scheduler driven phases:useEffect
hooks for post render setup, cleanup functions for unmount, and state/prop changes triggering re-renders. React lifecycles are framework controlled and not directly tied to explicit DOM operations.2. Update Handling
Web Components react only to explicit DOM changes, for example observed attribute modifications can be handled via an
attributeChangedCallback
callback method. Internal state changes in a Web Component don't automatically trigger re-renders unless you explicitly handle them this way. In contrast, React components re-render on every state/prop change by default, synchronizing the UI with the component's internal state through diffing and reconciliation.Registering and Integrating Web Components
Web Components operate like native HTML elements once registered. By importing the Web Component's JavaScript file, the custom element (
<task-item>
) becomes globally available. When the file is imported, it registers the element with the browser usingcustomElements.define()
, allowing you to use the tag anywhere in your React JSX as if it were standard HTML. This allows you to bridge framework boundaries while leveraging the browser's built-in component model. You've successfully integrated a framework-agnostic Web Component into the JSX of a React component. Observe how the browser renders the custom element seamlessly alongside React components. This demonstrates the first principle of Web Component integration: custom elements behave like standard HTML tags once registered. In the next step, you'll pass data into the Web Component to make it interactive. -
Challenge
Pass State with Attributes
Web Components can receive data through HTML attributes, which are always string values. To pass React state to a Web Component with HTML attributes you must:
1. Convert non-string values, like booleans, to strings
This can be achieved by calling the
toString
method when passing the value in an HTML attribute within your React component, for example:<data-card status={card.status.toString()}></data-card>
2. Declare observed attributes in the Web Component
You must declare the attributes for which your element needs change notifications. This can be achieved by defining a static field named
observedAttributes
with an array of the attribute names, for example:static observedAttributes = ["name", "enabled"];
3. Implement an
attributeChangedCallback
method to handle attribute updatesWhen attributes change, the Web Component should automatically updates its internal DOM. This can be achieved by defining an
attributeChangedCallback
callback method which will be called whenever an observed attribute is added, modified, removed or replaced. For example:attributeChangedCallback(name, oldValue, newValue) { switch (name) { case "name": this.textElem.textContent = newValue; break; case "enabled": const isEnabled = newValue === "true"; this.checkbox.checked = isEnabled; this.textElem.classList.toggle("enabled", isEnabled); break; } }
This allows you to establish one-way data flow from React to the Web Component. You've established one-way data flow from React to the Web Component using attributes. The Web Component now reacts to attribute changes, displaying the task text and completion state. This pattern maintains React as the state owner while delegating UI rendering to the Web Component. Next, you'll implement bidirectional data flow by handling events from the Web Component.
-
Challenge
Pass State with an Object
HTML attributes only support string values, creating limitations for complex data structures. To pass rich JavaScript objects:
- Use React's
ref
callback functionality to access Web Component instances directly (React documentation:ref
callback function). - Implement setter methods on Web Components to receive and use data.
- Pass objects without serialization/deserialization in HTML attributes.
This approach maintains data integrity and avoids conversion overhead. Consider an example implementation of this pattern:
Web Component implementation:
class DataCard extends HTMLElement { #cardData = null; set cardData(data) { this.#cardData = data; // Update DOM using this.#cardData } }
React integration of the
data-card
element using aref
callback function:<data-card ref={element => element.cardData = complexObject} />
- Use React's
-
Challenge
Pass State to React
Web Components communicate state changes through custom events. To establish bidirectional data flow between the Web Component and React:
-
Dispatch custom events from the Web Component with
bubbles: true
andcomposed: true
to cross shadow DOM boundaries. For example:this.dispatchEvent(new CustomEvent("statusChange", { detail: { id: 42 }, bubbles: true, composed: true }));
-
Use event delegation in React for dynamic elements, using DOM-level event listeners.
-
Leverage React's
useRef
anduseEffect
hooks for lifecycle management of the event listeners.
Here is an example of how the event handling might look in a React component:
const containerRef = useRef(); useEffect(() => { const handleUpdate = (event) => setState(event.detail.value); containerRef.current.addEventListener("statusChange", handleUpdate); return () => { containerRef.current.removeEventListener("statusChange", handleUpdate); }; }, []); return ( <div className="container" ref={containerRef}> <data-card></data-card> <data-card></data-card> <data-card></data-card> </div> );
This pattern maintains separation of concerns while enabling interaction and passing of state between the React component and the Web Component. You've established full bidirectional communication:
- Web Components dispatch custom events with rich data payloads.
- React handles events through delegated DOM listeners.
- Lifecycle hooks ensure proper cleanup.
This completes the integration pattern where React manages application state while Web Components handle UI rendering and interaction. This approach is now production-ready with efficient event handling and no memory leaks.
-
-
Challenge
Conclusion
Congratulations! You've successfully integrated a Web Component into a React application while navigating key differences in component lifecycles and data flow patterns. In this lab, you learned:
- How DOM-centric Web Component lifecycles (
connectedCallback
,attributeChangedCallback
) differ from React's state-centric model (useEffect
, automatic re-renders). - Techniques for one-way data flow via HTML attributes and observed properties.
- Object-based data transfer using refs and setters to preserve complex structures.
- Bidirectional communication through custom DOM events with proper cleanup.
The application you've implemented demonstrates production-ready integration patterns where React manages application state while Web Components handle rendering and UI logic. This hybrid approach unlocks the power of browser-native components within modern frameworks.
Experiment With Your New Skills
- Try adding a new
dueDate
attribute to<task-item>
and observe how React passes date strings vs including them in an object. - Modify custom event
detail
objects to include entire task objects instead of just IDs. - Experiment with Shadow DOM encapsulation by adding additional styles inside
task-item.js
.
Continue Your Learning Journey
- How DOM-centric Web Component lifecycles (
What's a lab?
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Provided environment for hands-on practice
We will provide the credentials and environment necessary for you to practice right within your browser.
Guided walkthrough
Follow along with the author’s guided walkthrough and build something new in your provided environment!
Did you know?
On average, you retain 75% more of your learning if you get time for practice.