How to use a Button component as a link in React

How to use a Button component as a link in React

ยท

3 min read

To correctly use a Button component as a link in React, we need to override the underlying button HTML tag of the component and turn it into an anchor tag, while still keeping the component styles and behaviour intact.

Let's explore in more detail.

Button component

First, let's build a typical Button component in React:

const Button = ({ onClick, children }) => {
  return (
    <button onClick={onClick} className="button">{children}</button>
  )
};

export default Button

And add some styles to it:

.button {
  background-color: #4CAF50;
  border: none;
  color: white;
  padding: 15px 32px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
}

Now, to use it, all we need to do is the following:

import Button from './Button'

const ComponentWithButton = () => {
  const handleClick = () => {
    // Some button logic
  }

  return (
     ... other JSX
    <Button onClick={handleClick}>Button</Button>
  )
}

This looks great so far! But what happens if we need to use this component not as a button, but as a link to another page or website?

Our first instinct might be to handle the navigation logic using the onClick handler. This is the wrong approach! Here's why:

  • The HTML of our page should be semantic - HTML elements have a specific purpose and should be used accordingly - a button should change something on the page while anchor tags are used for navigation;
  • Using the correct HTML tags is also good for accessibility - assistive technology benefits greatly from semantic HTML and so do users who navigate our website via keyboard;
  • Search engines take advantage of the links on our website to crawl it, so opting for an anchor tag is also SEO friendly.

So, if it's not recommended to use the onClick handler for navigation, what is the alternative?

Introducing React's as prop pattern

There is a mechanism to override the underlying HTML tag of our button component and to turn it into a link. Let's introduce an as prop to our Button component:

const Button = ({ onClick, children, as: Component = "button", ...rest }) => {
  return (
    <Component onClick={onClick} className="button" {...rest}>
      {children}
    </Component>
  );
};

With the help of the as prop, we can easily turn our button into a link:

import Button from './Button'

const ComponentWithLink= () => {

  return (
     ... other JSX
    <Button as="a" href="https://blog.whereisthemouse.com">Where is the mouse?</Button>
  )
}

The component above looks identical to our normal button, but is semantically a link. This is exactly what we wanted! ๐Ÿš€

๐Ÿ’ก Note: Our button is actually a link now. Because of this, there are some subtle differences it its behaviour. For example, if we use the keyboard to focus on a button, we can activate it with both Enter and Space key, while for a link only Enter will work.

Here's another scenario! What if we are using a router like React Router and want to link to a page that is internal to our website? Luckily, our component easily allows for this implementation:

import { Link } from 'react-router-dom"
import Button from './Button'

const ComponentWithInternalLink= () => {

  return (
     ... other JSX
    <Button as={Link} to="/">Home</Button>
  )
}

Once again, our component is styled exactly like a button but it is a React Router link underneath!

Conclusion

Let's conclude this article on a note of caution!

The approach above works nicely to convert our buttons into real semantic links. But before we use it, it is important to take a step back and consider whether this is something that we really want or need in the context of our React app.

While there are legitimate reasons for creating links that look and feel like buttons, in the majority of cases, we are better off using our React components and HTML elements as originally intended.

Happy coding! โœจ

Did you find this article valuable?

Support Where is the mouse? by becoming a sponsor. Any amount is appreciated!