Why You Should Use React Components Instead of HTML

HTML is the bedrock of the web. For decades, it has allowed developers to create structured documents and share content across the internet. But as websites have evolved into full-blown web applications, the limitations of HTML have become clear. Crafting complex, interactive experiences with HTML, CSS and JavaScript is possible but quickly gets cumbersome and unwieldy.

This is where React comes in. React is a JavaScript library for building user interfaces that has taken the web development world by storm since its release in 2013. React‘s key innovation is the component – a modular, reusable piece of UI that bundles its own structure, style and behavior. Components allow developers to break apart their UI into independent chunks to be composed, tested and reused throughout their application.

In this article, we‘ll dive into why React components are a such a powerful tool for building web UIs compared to vanilla HTML. We‘ll explore React‘s component model in depth and see how it enables developers to create UIs that are more maintainable, flexible and performant than ever before. By the end, you‘ll understand the benefits of thinking in components and how it will change the way you build for the web.

The Challenges of Building Web Apps with HTML

To understand why React components are so revolutionary, we first need to examine the problems of building complex web applications with vanilla HTML, CSS and JavaScript.

Consider a common scenario: an e-commerce site with a responsive product listing page containing a grid of products with images, titles, ratings and an Add to Cart button. The product data is loaded via an API.

Implementing this with HTML might look something like:

<div class="product-list">
  <div class="product">
    <img src="product1.jpg" alt="Product 1">
    <h3>Product 1 Title</h3>
    <p>Rating: 4.8</p>
    <button>Add to Cart</button>
  </div>
  <div class="product">
    <img src="product2.jpg" alt="Product 2">
    <h3>Product 2 Title</h3>    
    <p>Rating: 4.5</p>
    <button>Add to Cart</button>
  </div>
  <div class="product">
    <img src="product3.jpg" alt="Product 3">
    <h3>Product 3 Title</h3>
    <p>Rating: 4.9</p>
    <button>Add to Cart</button>
  </div>
</div>

This HTML defines the structure of our product list. To populate it with real data from an API, we‘d need to write JavaScript code to fetch the data, iterate over the results and insert them into the appropriate DOM nodes:

fetch(‘https://api.com/products‘)
  .then(response => response.json())
  .then(products => {
    const productList = document.querySelector(‘.product-list‘);

    products.forEach(product => {
      const productDiv = document.createElement(‘div‘);
      productDiv.classList.add(‘product‘);

      const img = document.createElement(‘img‘);
      img.src = product.image;
      img.alt = product.title;

      const title = document.createElement(‘h3‘);
      title.textContent = product.title;

      const rating = document.createElement(‘p‘);
      rating.textContent = `Rating: ${product.rating}`;

      const button = document.createElement(‘button‘);
      button.textContent = ‘Add to Cart‘;

      productDiv.append(img, title, rating, button);
      productList.appendChild(productDiv);
    });
  });

This code works, but it‘s tedious and error-prone. We have to manually construct DOM nodes, set attributes and properties, and insert them into the right places in the DOM tree. We‘re mixing the structure of our UI with the logic for populating it with data.

Now imagine we need to add sorting, filtering and pagination to our product list. Or handle loading and error states. Or make the Add to Cart button open a modal. Or ensure the UI stays in sync when the data changes. Suddenly our humble product list has ballooned into a tangled mess of DOM manipulation and event handling.

As our application grows in size and complexity, this approach becomes increasingly untenable. We end up with a fragile, monolithic codebase where everything is tightly coupled together. Changing one part of the UI risks breaking another. Reusing pieces of UI across different pages or projects is difficult. Testing and maintaining the app becomes a nightmare.

Components to the Rescue

This is where React‘s component model shines. Instead of describing our UI as one big chunk of HTML and manually wiring it up with JavaScript, React lets us break our UI into modular, reusable components that encapsulate their own structure, style and behavior.

A React version of our product list UI might look like this:

function ProductList({ products }) {
  return (
    <div className="product-list">
      {products.map(product => (
        <ProductListItem key={product.id} product={product} />
      ))}
    </div>
  );
}

function ProductListItem({ product }) {
  return (
    <div className="product">
      <img src={product.image} alt={product.title} />
      <h3>{product.title}</h3>
      <p>Rating: {product.rating}</p>
      <AddToCartButton productId={product.id} />
    </div>
  );
}

function AddToCartButton({ productId }) {
  function handleClick() {
    console.log(`Adding product ${productId} to cart`);
  }

  return <button onClick={handleClick}>Add to Cart</button>;
}

Here, we‘ve defined three React components – ProductList, ProductListItem, and AddToCartButton – each responsible for a specific part of the UI.

The ProductList component receives an array of products as a prop and maps over them, rendering a ProductListItem component for each one. The ProductListItem component receives an individual product as a prop and renders its image, title, rating, and an AddToCartButton. Finally, the AddToCartButton component renders a button that logs a message when clicked.

Notice how each component is focused on doing one thing well. The ProductList is only concerned with iterating over the products array and rendering ProductListItems. The ProductListItem only cares about displaying the details of a single product. The AddToCartButton encapsulates the behavior of the button click. Each component is self-contained, receiving data and callbacks via props and emitting UI as JSX.

This component-based architecture has several key benefits:

  • Reusability: Components are the basic building blocks of React UIs. Well-designed components can be reused across your application or even in entirely separate projects. Got another page that shows a list of products? Reuse the ProductList and ProductListItem components.

  • Composability: Components are inherently composable. They can be nested inside each other and combined in various ways to create complex UIs. Our ProductList composes ProductListItems, which compose an AddToCartButton. As your UI grows, you can create new components that reuse and compose existing ones.

  • Encapsulation: Components provide a natural way to encapsulate related markup, style and behavior together in one place. They are self-contained and can be developed, tested and modified in isolation from the rest of the app. This makes reasoning about and maintaining your UI much easier.

  • Declarative: Components allow you to describe your UI as a function of your state. Instead of imperatively modifying the DOM, you simply declare what your UI should look like given the current data. When the data changes, React automatically updates the DOM to match. This declarative approach is more predictable and less error-prone than manual DOM manipulation.

One-Way Data Flow

Another key feature of React‘s component model is one-way data flow. In React, data flows down the component tree from parent to child via props. When a parent component‘s state changes, it triggers a re-render of the component and its children.

This unidirectional flow of data makes the behavior of your UI more predictable. Given a component‘s props, you know exactly what it will render. Child components can only affect their parents through callback functions passed down as props.

Contrast this with two-way data binding in frameworks like Angular, where data can flow in both directions between parent and child components. This can make it harder to reason about how data is changing and can lead to subtle bugs.

One-way data flow also encourages a more declarative, functional style of programming. Instead of directly mutating data, you describe how the UI should change in response to user interactions and state changes. This leads to UIs that are easier to understand and less prone to bugs.

The Virtual DOM

Under the hood, React achieves its performance and declarative API through a concept called the virtual DOM. When a component‘s state changes, React builds a new virtual DOM tree representing the updated UI. It then diffs this virtual DOM against the previous one to determine the minimal set of changes needed to update the real DOM.

This diffing process is highly optimized and allows React to update the UI in a very efficient way. Only the components that actually need to change get re-rendered, and React batches DOM updates to minimize repaints and reflows.

The virtual DOM also enables React‘s declarative API. Instead of having to worry about when and how to update the DOM, you simply declare what the UI should look like at any given state, and React takes care of the rest.

While it‘s not necessary to understand the virtual DOM to use React, it‘s a key part of what makes React fast and efficient. It‘s also a great example of how React‘s unidirectional data flow and declarative API enable powerful abstractions that would be difficult to achieve with plain HTML and JavaScript.

Thinking in Components

Learning to "think in components" is perhaps the biggest mindset shift when moving from vanilla HTML/JS to React. Instead of thinking about your UI as a monolithic blob of markup and code, you learn to break it down into modular, reusable pieces that can be composed together.

This process involves looking at a design mockup and identifying the natural boundaries and responsibilities of each piece of the UI. A good rule of thumb is the single responsibility principle – each component should do one thing and do it well.

For example, consider a comment feed with a list of comments, a form for adding new comments, and a sorting dropdown. In plain HTML, you might have one giant <div> containing all the markup for the entire feed.

But in React, you would break this down into several smaller components:

  • <CommentList>: Responsible for rendering a list of comments
  • <Comment>: Responsible for rendering an individual comment
  • <CommentForm>: Responsible for rendering the form for adding new comments
  • <SortDropdown>: Responsible for rendering the sorting dropdown and handling the sorting logic

Each of these components would be self-contained, receiving data via props and emitting events via callbacks. The top-level <CommentFeed> component would compose these components together and manage the overall state of the feed.

function CommentFeed({ comments, onAddComment, sortOrder, onChangeSortOrder }) {
  return (
    <div>
      <CommentList comments={comments} />
      <CommentForm onSubmit={onAddComment} />
      <SortDropdown 
        sortOrder={sortOrder} 
        onChange={onChangeSortOrder} 
      />
    </div>
  );
}

By breaking the UI into components like this, you end up with a more modular, maintainable codebase. Each component can be developed, tested and updated independently of the others. And if you need to add a new feature or change the design, you can do so by modifying or creating individual components rather than having to wade through a massive blob of HTML and JavaScript.

Real-World Impact

Since its release in 2013, React has had a profound impact on the way developers build web applications. Its component-based model and declarative API have influenced the design of countless other libraries and frameworks, including Vue, Angular, Svelte, and Flutter.

Today, React is one of the most popular and widely-used JavaScript libraries in the world. According to the State of JS 2021 survey, React is used by 80% of respondents, far outpacing other libraries and frameworks. It also had the highest satisfaction rating, with 89% of users saying they would use it again.

This popularity is not just among hobbyists and small projects. React is used in production by some of the biggest tech companies in the world, including Facebook, Netflix, Airbnb, and Uber. It has been battle-tested in large-scale, high-traffic applications and proven its ability to deliver fast, reliable, and maintainable UIs.

For example, Airbnb has credited React with allowing them to unify their codebase across web and mobile, reuse components across projects, and improve the performance and reliability of their UI. Netflix has said that React‘s declarative API and server-side rendering capabilities were key to their ability to serve pages faster and provide a better user experience.

React has also spawned a huge ecosystem of libraries, tools, and best practices. From state management libraries like Redux and MobX, to testing frameworks like Jest and Enzyme, to development tools like Create React App and Storybook, the React ecosystem has evolved to support the needs of modern web development.

This ecosystem, combined with React‘s popularity and proven track record, has made it a valuable skill for web developers to learn. According to the 2021 Stack Overflow Developer Survey, React is the second most popular web framework among professional developers, behind only jQuery. And with the rise of mobile and desktop frameworks like React Native and Electron, React skills are becoming increasingly transferable across platforms.

Conclusion

HTML is not going away anytime soon. It will always be the foundation upon which the web is built. But for building modern, interactive web applications, React‘s component model offers a better way forward.

By breaking your UI into self-contained, reusable components, you can create more maintainable, scalable, and performant applications. React‘s declarative API and unidirectional data flow make reasoning about your UI simpler and less error-prone. And its virtual DOM and reconciliation algorithm provide a fast and efficient way to update the UI in response to state changes.

But React is more than just a library or a set of technical features. It represents a fundamental shift in how we think about building user interfaces. It challenges us to break apart our monoliths, to think in terms of composable pieces, and to elevate our UI code to the same level of abstraction and reusability as the rest of our application.

This mindset shift is not always easy, but it is incredibly powerful. As more and more developers adopt React and its component-based model, we are seeing a renaissance in frontend development. We are building UIs that are more dynamic, more interactive, and more seamlessly integrated with the rest of our application logic.

So if you‘re still building UIs with vanilla HTML, CSS, and JavaScript, I encourage you to give React a try. Start small, build a few components, and see how it changes the way you think about your UI. I think you‘ll find that once you start thinking in components, you‘ll never want to go back.

Similar Posts