Editor’s note: This article on web components vs. React was last updated on 7 March 2023 to reflect changes to React and React Hooks. This update also includes new sections on the pros and cons of web components and React and interactive code examples.
In this article, we will be discussing and comparing web components and React. Before moving ahead, it’s important to note that React and web components serve different purposes. Web components allow us to write reusable and strongly encapsulated custom HTML elements in webpages. React is a declarative JavaScript library that lets us create web app frontends with a component-based development pattern.
In this article, we will cover development concepts, features, and libraries provided for styling in React and web components and their accessibility. Additionally, we’ll explore the pros and cons of each technology and summarize with differences that help you select one for your next development requirement.
Jump ahead:
As already explained, we can write reusable UI elements with web components. You might think that we could do the same using React as well. For example, we can create and reuse a component somewhere else in the project. However, there’s a difference. A React component can only be reused in the React application. On the other hand, a web component can be used in any HTML document or any frontend library.
Web components can be used in React, Angular, Vue, etc., as it is included in the HTML specification and is native. For example, a custom header element created using web components can be used across various libraries and frameworks. With that being discussed, let’s talk about the features and concepts provided by React and web components.
The web components specification offers a way to create reusable custom HTML elements with three inbuilt technologies.
With custom elements, we can create a new customized HTML tag. It uses the browser’s JavaScript API customElements.define()
method to do that. A custom element is a JavaScript class that extends HTMLElement
. Moreover, custom elements require a hyphen in their names so the HTML parser can recognize them.
Consider the following example:
<!DOCTYPE html> <html> <head> <title>Custom Element</title> </head> <body> <div> <h1>Custom Element</h1> <test-element></test-element> </div> <script type="text/javascript"> class TestElement extends HTMLElement { constructor() { super(); this.setAttribute('name', 'John Doe'); // Setting an attribute } connectedCallback() { this.innerHTML = `<div><p>Hello ${this.getAttribute('name')}!</p><p>Nice to meet you</p></div>`; } } // Register the custom element customElements.define('test-element', TestElement); </script> </body> </html>
See the Pen
Custom elements demo by Shalitha Suranga (@shalithasuranga)
on CodePen.
Here, we defined a custom element <test-element>
with web component APIs. The custom element sets an attribute and renders some HTML content as follows:
The shadow DOM allows you to create an isolated custom element that doesn’t clash with the other parts of the webpage. For example, document.querySelector()
won’t return nodes defined in a shadow DOM. Moreover, styles defined in the shadow DOM are contained in it, for instance, scoped CSS.
If you change the background color of a div
to grey in a shadow DOM, then only that div
’s background will get changed, and no div
s outside of it will be affected. Look at the following source code:
<!DOCTYPE html> <html> <head> <title>Custom Element</title> </head> <body> <div> <h1>Shadow DOM</h1> <test-element></test-element> </div> <div> <p>Hello John Doe!</p> <p>Nice to meet you</p> </div> <script type="text/javascript"> class TestElement extends HTMLElement { constructor() { super(); this.setAttribute('name', 'John Doe'); // Setting an attribute const shadowRoot = this.attachShadow({ mode: 'open' }); // Attaches a shadow DOM tree to <test-element> and returns a reference } connectedCallback() { this.shadowRoot.innerHTML = `<style> div { background: #eee; padding: 12px; } </style> <div> <p>Hello ${this.getAttribute("name")}!</p><p>Nice to meet you</p> </div>`; } } // Register the custom element customElements.define('test-element', TestElement); </script> </body> </html>
See the Pen
Shadow DOM demo by Shalitha Suranga (@shalithasuranga)
on CodePen.
In the code above, we isolated some CSS using the shadow DOM tree concept. The code only applies CSS styles for the custom element’s div
s:
HTML templates allow us to declare markup structures at the load time using the <template>
tag. This element and its content will not render until we get its reference and attach it to the DOM manually, allowing us to add dynamic data. Therefore, if we have to use some markup structure at various times, we can use templates and avoid repeating the same code.
For example, look at the following source code:
<!DOCTYPE html> <html> <head> <title>Custom Element</title> </head> <body> <div> <h1>Templates</h1> <template id="student_template"> <style> li { color: #333; list-style: none; padding-bottom: 8px; } </style> <li> <span class="name"></span> — <span class="score"></span> </li> </template> <test-element></test-element> <ul id="students"></ul> </div> <script type="text/javascript"> class TestElement extends HTMLElement { constructor() { super() const shadowRoot = this.attachShadow({ mode: 'open' }); } connectedCallback() { let students = [{ name: "Emma", score: 90 }, { name: 'Ashton', score: 70 }, { name: 'Hannah', score: 85 }, { name: 'Steve', score: 90 }, { name: 'Amy', score: 75 }, ] students.forEach(student => { let template = document.getElementById('student_template'); let templateContent = template.content.cloneNode(true); // Append a clone of the template content to the shadow root this.shadowRoot.appendChild(templateContent); // Add the name this.shadowRoot.querySelector('.name').innerHTML = student.name; // Add the score this.shadowRoot.querySelector('.score').innerHTML = student.score; // Append the list to the ul students document.getElementById('students').appendChild(this.shadowRoot); }) } } // Register the custom element customElements.define('test-element', TestElement); </script> </body> </html>
See the Pen
Templates demo by Shalitha Suranga (@shalithasuranga)
on CodePen.
The above code renders a student list inside a custom element by using an HTML template:
React components are not instances of HTMLElement
like web components — they are special JavaScript objects for the React library to construct the virtual DOM tree. JSX-based React components get transpiled into React.createElement
function calls. JSX-based React components create React elements for rendering real DOM elements. React’s reconciliation process renders real DOM elements based on React elements on the virtual DOM.
There are two syntactical approaches for creating React components: class components and functional components.
A class component is a JavaScript class that extends the React.Component
class. It takes props, manages the state of a component, and returns the JSX code through the render
function. Props contain the data passed from the parent component. The component’s data can be stored in the state object. Whenever the state changes, the component re-renders. It also contains lifecycle methods that run at different points from the creation of the components until it gets destroyed.
For example, constructor()
is the first method that gets called when a component’s instance is initiated; therefore, the state is initialized here. Let’s see an example:
import React from 'react'; import './styles.css'; class App extends React.Component { constructor(props) { super(props); this.state = { value: 'Touch me' }; } buttonHandler = ()=> { if (this.state.value === 'Touch me') { this.setState({value: 'Touched!'}) } else { this.setState({value: 'Touch me'}) } } render() { return( <div> <h1>My Component!</h1> <button onClick={this.buttonHandler} className="button">{this.state.value}</button> </div> ); } } export default App;
The above React component renders a button
that changes the state with user click
events, as shown in the following preview:
Simply put, a functional component is a JavaScript function that returns the JSX code. Previously, functional components were purely presentational components because they did not have support for state management. However, with React Hooks, we can now have state and lifecycle methods as well.
Let’s do the above example using a functional component:
import React, {useState} from 'react'; import './styles.css'; const App = (props) =>{ // Creating a state using the useState hook const [value, setValue] = useState("Touch me"); const buttonHandler = () => { if (value === 'Touch me') { // Set the value to Touched! if it is Touch me, changing state setValue('Touched!'); } else { // Changing state setValue('Touch me'); } } return ( <div> <h1>My Component!</h1> <button onClick={buttonHandler} className="button">{value}</button> </div> ) } export default App;
The above code renders the same button
we’ve created before:
When it comes to building components, UI is a big part of it. You always want to create beautiful, engaging, and user-friendly components. However, styling it yourself from scratch can be tedious and time-consuming. Moreover, in React, it gets more difficult because it does not have a scoped style like web components do. So, you always look for external modules that can do that work for you.
React has a large ecosystem. Therefore, it has many UI libraries and frameworks compared to web components. For example, you can use the following UI kits in React without writing code and CSS for generic components, such as buttons
, dropdowns
, etc.:
Material-UI is one of the most popular UI frameworks in React. It implements the Material Design system that was created and maintained by Google. It provides several UI components, styles, themes, layouts, and icons. It is versatile, and continuous work and effort are being put in to keep improving it. You can browse all pre-developed components from the official documentation.
React Bootstrap is another popular React UI framework that replaces the Bootstrap JavaScript and has Bootstrap at its core. It provides various components, theming support, layouts, etc. It also has a large, growing development community and pre-developed Bootstrap-styled React components. You can browse all supported components from the official documentation.
It is the React integration for the Semantic UI. It provides many cool custom components; for example, Button
, Container
, Loader
, Input
, and more. It has the same styling system and theming as the Semantic UI. So, if you know it, you do not need to learn anything new. Moreover, it has sub-components that allow us to access the underlying markup. Because of that, we get flexibility in customizing elements. You can read more about all pre-developed React components from its official documentation.
If you decide to write your own styling for atomic components and don’t use UI kits, you can productively write styling definitions with styled-components or Sass-like CSS pre-processors.
Web components let you embed isolated style sheets for each custom element via the shadow DOM concept. The web components API is typically a bit complex because it offers a somewhat low-level, fully-featured API to create and manage web components. So, you can create web components productively with a library like Lit and define CSS in an easy way. Also, you can use pre-developed web components directly from the libraries below.
This is the web component version of the Material-UI framework. However, it is still a work in progress. This project’s maintainers are planning for a stable release, and you can check the progress from the GitHub repository.
There are modules available that allow you to use Bootstrap in your web components. However, some are still unstable or under development, and others do not provide as many features as react-bootstrap does. For example, bootstrap-webcomponents is a rewrite of Bootstrap for web components, but it is still in development mode. Aybolit Bootstrap provides a set of web components inspired by Bootstrap but does not have a wide range of components and features.
Eliz is an open collection of ready-to-use web components for common UI patterns, such as carousels, buttons, menus, and more. It also allows you to customize them or create new elements based on existing Elix components. You can browse all pre-developed web components from the official website here.
When we talk about the accessibility of a website, we mean everyone can easily use it. If our website is created with React or web components, these components must be accessible. So, the question arises, are they?
React allows us to create websites that are completely accessible. It can often be done using standard HTML techniques. For example, we can use the aria-*
attributes in JSX in the same way we use them in plain HTML, meaning they do not need to be camelCased
. In React, we add an extra div
to wrap all elements, but it will break HTML semantics, won’t it? Well, it does, but we can easily solve this problem by using React fragments (<></>
).
Another aspect of accessibility is that the application should be able to be used with the keyboard only. Now, React continuously changes the HTML DOM during runtime, and because of that, the keyboard focus can get lost. However, we can programmatically set the focus in various lifecycle
methods of the component. Furthermore, we can change titles whenever the screen (component) changes using the react-document-title
. React UI kit developers can implement accessibility for pre-developed components via react-aria, which offers accessible UI primitives through React Hooks.
Web components are also accessible because a custom element can extend any native HTML element, and thus, it inherits all of its characteristics, including accessibility features. Moreover, there can be a misunderstanding that elements created inside a shadow DOM won’t be accessible. However, this is not the case; screen readers can easily access the content.
Every UI library or framework typically comes with various pros and cons. Let’s summarize the pros and cons of using React to build web apps. Look at the following table:
Pros of using React | Cons of using React |
---|---|
An optimized, modern way to build fast web app frontends with the virtual DOM technology | Sometimes, developers need to write more code than they initially expect |
Fully-featured boilerplates/starter kits availability (Create React App, Vite, etc.) | Easy to learn the basics, but some developers find it’s hard to master some React concepts (optimizations) |
Cross-platform app development with React Native | Library releases sometimes make existing codes outdated (functional components standard and Hooks) |
Availability of many community packages | React isn’t a framework — it’s a library — so you typically need to use other dependencies (react-router) |
Better developer support, documentation, and community resources for learning purposes | |
Motivates to use better engineering practices, such as reusability, performance, and code quality |
We can use the standard web components API or a web components library to create web components in our apps. Also, we can build an entire app with web components or develop a part of it. Look at the following table that explains the pros and cons of web component technology:
Pros of using Web Components | Cons of using Web Components |
---|---|
A native, inbuilt component-based development concept for modern web browsers | Somewhat complex, low-level standard API |
Framework agnostic — you don’t need a specific framework/library for rendering web components | The third-party library ecosystem is still growing compared to libraries like React |
Availability of developer-friendly abstractions (easy ways to create web components). Using Lit, Stencil, etc. | Not as many UI kits compared to popular frontend frameworks |
Easy to reuse even over the network. A better solution for creating widgets and implementing micro-frontends | Community-based learning resources are still growing compared to other technologies |
Ability to use directly with other DOM APIs (No need to use refs-like implementations as we typically find in React) | |
A native way to create isolated, custom HTML elements |
From the developer’s perspective, web components also let you define reusable components as React does. But, the goals of React and web components are so different. The built-in web components browser feature offers a somewhat low-level API to create custom HTML elements. It doesn’t offer a fully-featured, component-based UI framework or library-like app-developer-focused features.
In the past, developers used traditional div
s to render external remote widgets and hidden div
s or HTML strings as content templates. Also, sharing code in vanilla JavaScript apps (apps written with native DOM APIs) in an OOP way was hard. Web components solved these issues natively by letting developers create their own reusable HTML elements directly in the DOM tree with HTML templates.
React aims to offer a productive, declarative development methodology to create component-based highly dynamic UIs. As you can see, web components and React came with two different goals, so the native web components technology won’t compete with React or replace it. But, the growth of web component-based libraries may affect React’s popularity.
For example, web components are faster and more memory-efficient than React (according to this benchmark) because they are browser-native. But creating web components using the standard API is complex and not so developer-friendly compared to JSX-based React. However, the Lit-like libraries let you create web components in a React-like simple way.
So, even though web components API won’t replace React, the growth of web components-based UI libraries can become so competitive with React’s popularity, depending on web component libraries’ popularity, developer support, and availability of third-party packages like lit-element-router.
React and web components are two different technologies and solve different problems. React offer a set of APIs to create component-based UIs. On the other hand, the web component API is a native browser standard for creating reusable custom HTML elements. So, we cannot directly compare both and recommend one to use. Instead, we can summarize with the following comparison and help you select an option for building an app, widget, or component:
React | Web components |
---|---|
It is a JavaScript library for building UIs | It is an inbuilt native browser API that allows us to create reusable and isolated custom HTML elements |
Offers two syntactical ways to define components: class-based and functional | Offers three technologies for creating components: custom elements, shadow DOM, and HTML templates |
Component re-rendering based on state changes | No inbuilt state management system |
Components are directly (without a conversion) reusable in React only | Framework agnostic components (can be used with any frontend library or directly in HTML documents) |
No local styling | CSS can be scoped using shadow DOMs |
React provides a unidirectional data flow pattern | You have to implement data binding yourself |
Its library ecosystem is incredibly vast and powerful. It has a large community and forever-growing tools and libraries. | The library ecosystem is still growing and limited |
No abstraction is needed to build apps — you can directly use the official React APIs | The standard API is somewhat complex and low-level, so many developers tend to use libraries like Lit |
You can even use these two technologies together based on your development requirements. It’s possible to use web components inside a React component and React components inside a web component. Learn more about combining React with web components from the official React documentation.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowThe useReducer React Hook is a good alternative to tools like Redux, Recoil, or MobX.
Node.js v22.5.0 introduced a native SQLite module, which is is similar to what other JavaScript runtimes like Deno and Bun already have.
Understanding and supporting pinch, text, and browser zoom significantly enhances the user experience. Let’s explore a few ways to do so.
Playwright is a popular framework for automating and testing web applications across multiple browsers in JavaScript, Python, Java, and C#. […]
6 Replies to "Web components vs. React"
You forgot to say we can pass easily data to react components while it is hard to do for webComponents. Only primitive arguments are supported and it is a pain to pass functions, objects or class instances.
-Types of (React) components include functional and class. That’s not two types, but two ways of doing the same.
-Types of (web) components include custom elements, shadow DOM, and templates. Shadow DOM it’s a property of custom elements and templates are not part of web components at all.
-Components are reusable in React only. As long as they may be compiled as web components itself, how it’s this possible? I’m even using React components in Angular (without WC build).
-The ecosystem is not as vast as React’s ecosystem. How can a Web standard not have a good ecosystem?
-(React) It is a JavaScript library for building UI interfaces for single-page applications. That’s what it was intended to be, but people build entire SPAs with it, not just UI.
-It is a set of different technologies that allow us to create reusable and isolated elements. It’s not a set of technologies, as it’s a Web standard and JavaScript is the only technology used. (Of course there are options that allow you to use more comfortable alternatives, like stencil, lit-element…)
You can package the entire react componen as a JS lib and reuse in any App. Good ir bad idea… But you can do It.
yeah, usually we have done with that
You are wrong here I am afraid. Web components have a JS interface which allows properties and any other complex argument to be passed.
Many wc helper libraries like lit-element also provide convenience mechanisms to do this in just the same way you would in react.
Good point. It is definitely more complicated than React.