171

Using suggested method: This is the result: A link in the button, Code in between comment lines

I was wondering if there is a way to wrap a Link element from 'react-router' in an HTML button tag using react.

I currently have Link components to navigate pages in my app, but I would like to map that functionality to my HTML buttons.

enter image description here enter image description here

SherylHohman
  • 16,580
  • 17
  • 88
  • 94
Jose Rivera
  • 1,821
  • 2
  • 12
  • 8

19 Answers19

269

While this will render in a web browser, beware that:
⚠️Nesting an html button in an html a (or vice-versa) is not valid html ⚠️. If you want to keep your html semantic to screen readers, use another approach.

Do wrapping in the reverse way and you get the original button with the Link attached. No CSS changes required.

 <Link to="/dashboard">
     <button type="button">
          Click Me!
     </button>
 </Link>

Here button is HTML button. It is also applicable to the components imported from third party libraries like Semantic-UI-React.

 import { Button } from 'semantic-ui-react'
 ... 
 <Link to="/dashboard">
     <Button style={myStyle}>
        <p>Click Me!</p>
     </Button>
 </Link>
Rui Fonseca
  • 1,112
  • 1
  • 6
  • 6
Naren Yellavula
  • 7,273
  • 2
  • 29
  • 23
151

LinkButton component - a solution for React Router v4

First, a note about many other answers to this question.

⚠️ Nesting <button> and <a> is not valid html. ⚠️

Any answer here which suggests nesting a html button in a React Router Link component (or vice-versa) will render in a web browser, but it is not semantic, accessible, or valid html:

<a stuff-here><button>label text</button></a>
<button><a stuff-here>label text</a></button>

Click to validate this markup with validator.w3.org

This can lead to layout/styling issues as buttons are not typically placed inside links.


Using an html <button> tag with React Router <Link> component.

If you only want an html button tag…

<button>label text</button>

…then, here's the right way to get a button that works like React Router’s Link component…

Use React Router’s withRouter HOC to pass these props to your component:

  • history
  • location
  • match
  • staticContext

LinkButton component

Here’s a LinkButton component for you to copy/pasta:

// file: /components/LinkButton.jsx
import React from 'react'
import PropTypes from 'prop-types'
import { withRouter } from 'react-router'

const LinkButton = (props) => {
  const {
    history,
    location,
    match,
    staticContext,
    to,
    onClick,
    // ⬆ filtering out props that `button` doesn’t know what to do with.
    ...rest
  } = props
  return (
    <button
      {...rest} // `children` is just another prop!
      onClick={(event) => {
        onClick && onClick(event)
        history.push(to)
      }}
    />
  )
}

LinkButton.propTypes = {
  to: PropTypes.string.isRequired,
  children: PropTypes.node.isRequired
}

export default withRouter(LinkButton)

Then import the component:

import LinkButton from '/components/LinkButton'

Use the component:

<LinkButton to='/path/to/page'>Push My Buttons!</LinkButton>

If you need an onClick method:

<LinkButton
  to='/path/to/page'
  onClick={(event) => {
    console.log('custom event here!', event)
  }}
>Push My Buttons!</LinkButton>

Update: If you're looking for another fun option made available after the above was written, check out this useRouter hook.

Beau Smith
  • 33,433
  • 13
  • 94
  • 101
63

Why not just decorate link tag with the same css as a button.

<Link 
 className="btn btn-pink"
 role="button"
 to="/"
 onClick={this.handleClick()}
> 
 Button1
</Link>
Beau Smith
  • 33,433
  • 13
  • 94
  • 101
Chase James
  • 833
  • 1
  • 7
  • 10
  • 1
    I will keep this as a last resort mostly because I have the CSS done for all the buttons that I need but thanks for the suggestion – Jose Rivera Feb 26 '17 at 00:40
  • Brilliant solution. For blueprint I do in this way: Link – caiiiycuk Jun 11 '20 at 14:37
  • 2
    You can't use enabled/disabled states on a fake button. – maco Dec 17 '20 at 02:10
  • 1
    This is perhaps the best approach, because navigation typically should use link semantics, i.e., `` tags. It should be noted though that simply applying an existing CSS class designed for `` to an `` can lead to broken style. The reason is that these elements are fundamentally different in terms of CSS behavior, see [this question](https://stackoverflow.com/q/66430085/1804173). In my case, I needed to refactor styles first to apply this solution. – bluenote10 Mar 01 '21 at 22:02
44

If you are using react-router-dom and material-ui you can use ...

import { Link } from 'react-router-dom'
import Button from '@material-ui/core/Button';

<Button component={Link} to="/open-collective">
  Link
</Button>

You can read more here.

Inam Ul Huq
  • 732
  • 7
  • 8
30

You can use useHistory hook since react-router v5.1.0.

The useHistory hook gives you access to the history instance that you may use to navigate.

import React from 'react'
import { useHistory } from 'react-router-dom'

export default function SomeComponent() {
  const { push } = useHistory()
  ...
  <button
    type="button"
    onClick={() => push('/some-link')}
  >
    Some link
  </button>
  ...
}

NOTE: be aware that this approach answers the question but is not accessible like @ziz194 says in their comment

this is not accessible though, as the button will not be a tag and thus it doesn't have link behaviours, like opening the link in a new page. It is also not optimal for screen readers.

CONCLUSION: The better way to handle this is to use an <a> tag and style it.

SandroMarques
  • 6,070
  • 1
  • 41
  • 46
20

Update for React Router version 6:

The various answers here are like a timeline of react-router's evolution

Using the latest hooks from react-router v6, this can now be done easily with the useNavigate hook.

import { useNavigate } from 'react-router-dom'      

function MyLinkButton() {
  const navigate = useNavigate()
  return (
      <button onClick={() => navigate("/home")}>
        Go Home
      </button>
  );
}
J M Rossy
  • 916
  • 7
  • 8
  • 4
    version 6 isn't available yet and this answer is confusing because of that. latest as of this comment is 5.2.0. – davidawad Dec 01 '20 at 17:33
  • version 6 is currently available in beta and being used in some projects. Please see the Versions tab here: https://www.npmjs.com/package/react-router – J M Rossy Dec 01 '20 at 17:36
10

With styled components this can be easily achieved

First Design a styled button

import styled from "styled-components";
import {Link} from "react-router-dom";

const Button = styled.button`
  background: white;
  color:red;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid red;
  border-radius: 3px;
`
render(
    <Button as={Link} to="/home"> Text Goes Here </Button>
);

check styled component's home for more

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Obed Amoasi
  • 1,837
  • 19
  • 27
8

I use Router and < Button/>. No < Link/>

<Button onClick={()=> {this.props.history.replace('/mypage')}}>
   HERE
</Button>
Alan
  • 9,167
  • 4
  • 52
  • 70
  • 2
    I think this should be the default solution, but perhaps some details are lost to me? _ANY_ component/node/element that support `onClick={ () => navigateTo(somePath) }` could use this approach. Whether using redux and `import {push} from 'connected-react-router'` or just `history.push` (or replace) like in your answer. – Thomas Fauskanger Jul 18 '18 at 12:27
  • this solution is not accessible, you cannot open the link in a new page using this solution. – ziz194 Aug 26 '21 at 07:01
7

For anyone looking for a solution using React 16.8+ (hooks) and React Router 5:

You can change the route using a button with the following code:

<button onClick={() => props.history.push("path")}>

React Router provides some props to your components, including the push() function on history which works pretty much like the < Link to='path' > element.

You don't need to wrap your components with the Higher Order Component "withRouter" to get access to those props.

pflevy
  • 731
  • 7
  • 8
  • This works fine for me; though I'm not sure what about this solution is specific to React 16.8+ or hooks. I think you're in good shape with this so long as you employ `BrowserRouter` (based on the HTML history API) instead of `HashRouter`. – pglezen Feb 17 '20 at 04:28
  • Navigation on click for non-Link elements is meh. You can't interact as you would with Link, so no "open in a new tab" features. – zcharles Apr 16 '21 at 05:04
6

⚠️ No, Nesting an html button in an html a (or vice-versa) is not valid html

Raphael Rafatpanah
  • 19,082
  • 25
  • 92
  • 158
3
<Button component={Link} to="/dashboard" type="button">
    Click Me!
</Button>
Manuel Malvar
  • 47
  • 1
  • 1
  • 1
    While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. You can find more information on how to write good answers in the help center: https://stackoverflow.com/help/how-to-answer . Good luck – nima Oct 17 '21 at 22:14
2

Using react-router-dom and a function

In react, you can use react-router-dom by applying the useHistory call...

- Firstly

import { useHistory } from 'react-router-dom';

- Secondly Inside your function...write a function to handle the button click

const handleButtonClick = () => {
    history.push('/YourPageLink')
  }

- Lastly

<button onClick={handleButtonClick} className="CSS">
   Button Text
</button>
1

Many of the solutions have focused on complicating things.

Using withRouter is a really long solution for something as simple as a button that links to somewhere else in the App.

If you are going for S.P.A. (single page application), the easiest answer I have found is to use with the button's equivalent className.

This ensures you are maintaining shared state / context without reloading your entire app as is done with

import { NavLink } from 'react-router-dom'; // 14.6K (gzipped: 5.2 K)

// Where link.{something} is the imported data
<NavLink className={`bx--btn bx--btn--primary ${link.className}`} to={link.href} activeClassName={'active'}>
    {link.label}
</NavLink>

// Simplified version:
<NavLink className={'bx--btn bx--btn--primary'} to={'/myLocalPath'}>
    Button without using withRouter
</NavLink>
Acts7Seven
  • 417
  • 1
  • 6
  • 14
1

I recommend that you utilize the component prop on the Link component. Using this, you can have effectively any component behave as an a component from the perspective of React Rotuer. For instance, you can create your own "Button" component and then utilize this.


const MyButton = () => {
    return <button>Do something with props, etc.</button>
}

<Link to="/somewhere" component={MyButton}>Potentially pass in text here</Link>
Anthony Dito
  • 3,610
  • 3
  • 29
  • 56
  • I agree, this is how it's meant to be used. Personally I would write it this way though: ` }>Pass in text here` – Marc Selman Oct 04 '21 at 13:57
0

As I searched through answers everyone was talking about the old versions of react-router-dom, so this might be useful answer for those who want to use the hooks in functional components. As in the new version few hooks are changed and even the withRouter is not applicable you need to make a custom HOC.

we can achieve it through functional components you can use your button and then describe onClick event to trigger to the next page through useNavigate hook.

React-Router-Dom version 6


this is only possible in functional components as i am using hooks, if you want to use on class components then you can make hoc.

import {Link, useNavigate} from 'react-router-dom';
function Home() {
you can pass your state like so:  navigate('/show',{state});
const toShowInfoPage=()=>{
navigate('/show');

  }


return(
<>


 /*you can use button as well or anything 
of your choice, but remember 
you have to pass the onClick event.
    */
 <a 
onClick={()=>{toShowInfoPage()}} 
style={{textDecoration:'none',
paddingRight:'20px',
color:'#187bcd',
cursor:'pointer'}}>


</>

)



}

To access useNavigate() with a class component you must either convert to a function component, or roll your own custom withRouter Higher Order Component to inject the "route props" like the withRouter HOC from react-router-dom v5.x did.


This is just a general idea. if you want to useNavigate Here's an example custom withRouter HOC:

const withRouter = WrappedComponent => props => {
  const params = useParams();
  // etc... other react-router-dom v6 hooks

  return (
    <WrappedComponent
      {...props}
      params={params}
      // etc...
    />
  );
};

And decorate the component with the new HOC.

export default withRouter(Post);

This will inject a props for the class component.

Sabaoon Bedar
  • 3,113
  • 2
  • 31
  • 37
0

You can use the component prop: https://v5.reactrouter.com/web/api/Link/component-reactcomponent

For example:

<Link to="/login" component={({ navigate}) => <button onClick={navigate}>Login</button>} />
cnt-null
  • 11
  • 2
0

For react-router-dom v5 you can simply style the link itself to look like button:

import {Link} from "react-router-dom";

<Link className="btn" to="/myurl">Submit</Link>

where className="btn" refers to the css class name, which makes it look like button.

Or you can define the following object:

export function ButtonLink(props) {
    const { as: Component, children, to, className } = props;
    return <Component className={className} to={to}>{children}</Component>
}

and then use it

import {ButtonLink} from "../../components/ButtonLink";
import {Link} from "react-router-dom";

<ButtonLink as={Link} to="/myUrl" className="btn">Submit</ButtonLink>

but you will still have to refer to css to make it look like button (className="btn"), so first approach is the easiest.

Benas
  • 2,106
  • 2
  • 39
  • 66
0

very simple - javascript part:

    <script>

    function gofor(link) {
        location.href = link;
    }

    </script>

and here the html part:

    <button onclick="gofor('link.html');">Click Me!</button>

Thats all.

Ensai Tankado
  • 189
  • 1
  • 4
0

Just useLinkClickHandler (for react-router version 6)

const ButtonLink = ({to, children, ...rest}) => {
  const handleClick = useLinkClickHandler(to);

  return (
    <button onClick={handleClick} {...rest}>
      {children}
    </button>
  );
};
Igor Sukharev
  • 2,467
  • 24
  • 21