Avoid overusing div elements in React components

The div element is the most generic HTML element. As a result, it gets overused in React components. HTML 5's semantic layout elements can help with this.
May 11, 2023React
7 min read

When looking over React code, I often see div elements being overused in React components. In HTML, the div element is short for division, and it can represent any type of block-level division on a page. Using div elements for everything is not wrong, but it's not great either.

A block-level HTML element always starts on a new line and always takes up the full width available.

The div element has been around for a while. It's become the go-to element of choice when we need to wrap some content in a block for layout or styling purposes. Since the div element is the most generic HTML element, it's easy to end up overusing it.

Consider the following React component that uses div elements for almost everything.

const App = () => {
  return (
    <div className="layout">
      <div className="header">
        <div className="title">My blog</div>
        <div className="navigation">
          <a href="/">Home</a>
          <a href="/about">About</a>
          <a href="/contact">Contact</a>
        </div>
      </div>      
      <div className="main">
        <div className="intro">
          Welcome to my blog.
        </div>
        <div className="content">
          <div className="article">
            <div id="article-header">
              <h3>My blog post</h3>
            </div>          
            <p>Read more</p>
          </div>
          <div className="related">
            <h3>Related posts</h3>
          </div>
        </div>
      </div>
      <div className="footer">
        Contact us!
      </div>
    </div>
  )
}

export { App }

That's a lot of div elements! There's nothing technically wrong with the HTML code above. It works fine. It can be styled as needed using the class names that are applied to the div elements. However, there are some serious concerns with this approach.

Issues with overusing div elements

The HTML Spec advises us to use div element as a last resort.

Authors are strongly encouraged to view the div element as an element of last resort, for when no other element is suitable. Use of more appropriate elements instead of the div element leads to better accessibility for readers and easier maintainability for authors.

Accessibility

A div is just a block-level wrapper. div tags don't communicate useful information about the structure of a web page.

a11y tools try their best to parse the structure of a web page and find meaning in that structure. However, div tags communicate nothing to a11y tools. They don't provide any meaning as to what sort of content one could expect to find in a given div.

As developers, we can recognize that an element with an id of article-header is intended to be the header of a blog article, but robots can't recognize that.

Readability

The code above is hard to read. It's very busy, with lots of HTML elements and class names. To make sense of each element, we must read through all the markup and get to the class names - which gives us an indication of what each element is for.

The nesting of div tags makes it hard to know what level of the element hierarchy we're currently reading. It also makes it hard to see if we missed an opening or closing tag.

Not standards-compliant

If only there was a standardized way to understand the purpose of each element on a web page - rather than just knowing it's a division. There now is a way! This way was provided to us with HTML 5. Using div tags everywhere was fine when HTML 4 was still around. However, the standards-compliant markup that powers the web is now HTML 5.

One of the great advancements that HTML 5 introduced was a standardized set of semantic elements.

Semantic elements to the rescue

The term semantic refers to the meaning of a word. Semantic elements allow us to structure our web pages in a more meaningful way. When we use a semantic element, we make it's purpose on the web page clear. Semantic elements are standardized as of HTML 5, which means they define a web page layout in a way that everyone can understand, including robots.

Semantic HTML is all about using the right HTML elements for their right purpose. Sure, we can use a combination of CSS and JavaScript to make any HTML element behave however we want. We can use a div element for a button instead of using a button element, for example. However, this is not semantic HTML.

Besides accessibility, the benefits of semantic HTML are:

  • Easier to develop with.
  • Easier to understand.
  • Results in less HTML elements, thus, a smaller file size.
  • Better for SEO.

When it comes to semantic HTML and SEO, search engines give more importance to keywords inside headings, links, etc, rather than keywords in non-semantic div elements.

Best of all, semantic HTML is not more complex to write than non-semantic HTML. If semantic HTML is used consistently across a project, there will be less refactoring to do later on.

Layouts with semantic HTML

When writing on a page, we usually divide that page into three areas: a header, a main content area, and a footer. The main content area is then divided into various sections. The same is true for semantic HTML.

Rather than creating a layout with only nested div elements, we can select more semantically appropriate HTML 5 elements. These elements will provided extra details to screen readers (and other robots) regarding the content that is being navigated.

Let's take a look at the semantic HTML 5 elements for web page layouts.

HTML 5 header element

The header element is for introductory content, titles, or a container navigation elements of a section or page.

<header>
  <h1>Welcome</h1>
</header>

HTML 5 nav element

The nav element is for navigation links.

<nav>
  <ul>
    <li>
      <a href="/">Home</a>
    </li>
    <li>
      <a href="/about">About</a>
    </li>
  </ul>
</nav>

HTML 5 article element

The article element is for self-contained content that can be independently distributed. An article should make sense on its own.

<article>
  <h3>Blog Post Title</h3>
  <p>Content...</p>
</article>

HTML 5 section element

The section element is for a standalone section or a grouping of content.

<section>
  <h2>Contact Us</h2>
  <p>Send a message</p>
</section>

HTML 5 aside element

The aside element is for accompanying content that is related to the main content, such as sidebars.

<aside>
  <h3>Related Articles</h3>
  <ul>
    <li>Article 1</li>
    <li>Article 2</li>
  </ul>
</aside>

The footer element is for the the closing section of a section or page.

<footer>&copy; My Website.</footer>

HTML 5 main element

The main element is for the main content of a page.

<header>
  My Site
</header>
<main>
  <h1>My page</h1>
  <p>My page content</p>
</main>
<footer>
  &copy; My Website.
</footer>

The HTML Spec indicates that we should not include more than one main element in a document. We should not include the main element as a child of an article, aside, footer, header or nav element.

Fixing the non-semantic HTML

With these semantic layout elements now in our HTML tool belt, let's re-visit the non-semantic example we saw above. We'll convert the div elements to semantic ones.

import { ReactNode } from "react";

type LayoutProps = {
  children?: ReactNode;
}

const Layout = ({ children }: LayoutProps) => {
  return (
    <>
      <header>
        <h1>My blog</h1>
        <nav>
          <a href="/">Home</a>
          <a href="/about">About</a>
          <a href="/contact">Contact</a>
        </nav>
      </header>
      <main>
        {children}
      </main>
      <footer>
        Contact us!
      </footer>
    </>
  )
}

const App = () => {
  return (
    <Layout>
      <section className="welcome">
        Welcome to my blog.
      </section>
      <section className="posts">
        <article>
          <header>
            <h3>My blog post</h3>
          </header>          
          <p>Read more</p>
        </article>
        <aside>
          <h3>Related posts</h3>
        </aside>
      </section>
    </Layout>
  )
}

export { App }

No more div elements! We were able to replace them all with semantic HTML elements.

For the top-most wrapping element, we introduced a Layout React component that makes use of the React children prop to render page content with a specific layout. This approach keeps the header, footer, and main content areas consistent across the entire site. It's great for theming and templating. The header and footer elements within the Layout component could also be moved to their own respective Header and Footer components.

We removed the class names and ids from the HTML elements. We only added class names where we need to distinguish between identical siblings. We had two sibling section elements, so we gave them a class name each. This will allow us to style them independently.

The result is an App component that is easier for developers and screen readers to read and understand.

New
Be React Ready

Learn modern React with TypeScript.

Learn modern React development with TypeScript from my books React Ready and React Router Ready.