A little semantic HTML trick for React components

Semantic HTML is the foundation of a web project. It helps assistive technologies and Google make sense of your site.

What is semantic HTML? article, heading, h2, aside, ul, section etc. all have semantic meaning. It says something about the content inside them. If the content is a stand-alone piece of content, use article; if it's a list, use ul, and so on. In comparison, div and span have no semantic meaning.

To lean more about check out web.dev's article on Semantic HTML.

Unfortunately, adherence to semantic HTML often gets lost when creating reusable React UI components.

Let's say we make a reusable card component.

It could look something like this:

// File: card.js
import React from "react";

export function Card(props) {
  const { children } = props;
  return <div>{children}</div>;
}

It makes sense not to give the design of the content (a card) any semantic meaning by using a div. However, such a component is very often used semantically.

  • As a list of blog posts => article
  • As a call out in an article => aside
  • To spice up the design of a paragraph => p

We can solve the issue by surrounding the card with the correct semantic element:

// File: exmaple.js
import React from "react";
import { Card } from "./card";

export function Example() {
  return (
    <article>
      <p>An introduction</p>
      <aside>
        <Card>
          Something related to the article but a little outside of the normal
          flow.
        </Card>
      </aside>
      <p>More content...</p>
      <p>
        <Card>A paragraph with different styling...</Card>
      </p>
      <p>More content...</p>
    </article>
  );
}

But we'll be notified that a div is not a valid child of p, and the approach feels iffy. At least to me 🤪

The semantic HTML trick for React components!

Make the semantic HTML element configurable:

// File: card.js
import React from "react";

export function Card(props) {
  // 1️⃣ Destructure element and children from props
  const { element, children } = props;

  // 2️⃣ Capitalise element to make it valid jsx
  let Element = element;

  // 3️⃣ Make "div" the default choice
  if (!element) {
    Element = "div";
  }

  return <Element>{children}</Element>;
}

Or in shorthand form:

import React from "react";

export function Card(props) {
  // Do 1️⃣, 2️⃣, 3️⃣ in one line
  const { element: Element = "div", children } = props;

  return <Element>{children}</Element>;
}

You may then use the Card component like so:

// File: exmaple.js
import React from "react";
import { Card } from "./card";

export function Example() {
  return (
    <article>
      <p>An introduction</p>

      <Card element="aside">
        Something related to the article but a little outside of the normal
        flow.
      </Card>

      <p>More content...</p>

      <Card element="p">A paragraph with different styling...</Card>

      <p>More content...</p>
    </article>
  );
}

Credit where credit is due. I learned how to do this by looking at MUI's source code!

 

All the best,
Queen Raae

Interested in more daily treasures like this one?
Sent directly to your inbox?

You might also be interested in...