144
const rootEl = document.getElementById('root');

ReactDOM.render(
    <BrowserRouter>
        <Switch>
            <Route exact path="/">
                <MasterPage />
            </Route>
            <Route exact path="/details/:id" >
                <DetailsPage />
            </Route>
        </Switch>
    </BrowserRouter>,
    rootEl
);

I am trying access the id in the DetailsPage component but it is not being accessible. I tried

<DetailsPage foo={this.props}/>

to pass parameters to the DetailsPage, but in vain.

export default class DetailsPage extends Component {
    constructor(props) {
        super(props);
    }

    render() {
        return (
            <div className="page">
            <Header />
            <div id="mainContentContainer" >

            </div>
            </div>
    );
    }
}

So any idea how to pass the ID on to the DetailsPage ?

M T
  • 4,099
  • 4
  • 21
  • 27

15 Answers15

145

I used this to access the ID in my component:

<Route path="/details/:id" component={DetailsPage}/>

And in the detail component:

export default class DetailsPage extends Component {
  render() {
    return(
      <div>
        <h2>{this.props.match.params.id}</h2>
      </div>
    )
  }
}

This will render any ID inside an h2, hope that helps someone.

Alexander Luna
  • 5,261
  • 4
  • 30
  • 36
  • It's a simpler and cleaner answer. However, there is a limitation. It doesn't let you pass any additional properties besides what is already there in the route properties object. The accepted answer does. – Anurag Apr 02 '21 at 06:42
123

If you want to pass props to a component inside a route, the simplest way is by utilizing the render, like this:

<Route exact path="/details/:id" render={(props) => <DetailsPage globalStore={globalStore} {...props} /> } />

You can access the props inside the DetailPage using:

this.props.match
this.props.globalStore

The {...props} is needed to pass the original Route's props, otherwise you will only get this.props.globalStore inside the DetailPage.

Win
  • 2,083
  • 2
  • 11
  • 14
  • 3
    But why would it not work the way I specified in the ask ? – M T Aug 30 '17 at 13:50
  • 4
    First, your code was wrong. Placing a component inside a Route like `` was not the same as ``. Your code rendered `DetailsPage` even if you visited '/'. Second, when you wrote ``, `this` referred to `null`. That's why it didn't work. – Win Aug 30 '17 at 14:13
  • This gives an error `Warning: [react-router] You cannot change ; it will be ignored` – Hareesh Apr 17 '18 at 10:19
  • Please open a new question and share your code there. – Win Apr 18 '18 at 07:16
  • Please refer to @Alexander Luna 's answer below for a simpler solution. – Fed Apr 23 '21 at 08:16
88

Since react-router v5.1 with hooks:

import { useParams } from 'react-router';

export default function DetailsPage() {
  const { id } = useParams();
}

See https://reacttraining.com/blog/react-router-v5-1/

user1338062
  • 11,939
  • 3
  • 73
  • 67
41

Use render method:

<Route exact path="/details/:id" render={(props) => (
    <DetailsPage id={props.match.params.id}/>
)} />

And you should be able to access the id using:

this.props.id

Inside the DetailsPage component

tsi
  • 1,244
  • 12
  • 10
Steven Daniel Anderson
  • 1,435
  • 2
  • 11
  • 17
14

Use the component:

<Route exact path="/details/:id" component={DetailsPage} />

And you should be able to access the id using:

this.props.match.params.id

Inside the DetailsPage component

Seraf
  • 850
  • 1
  • 17
  • 34
Dekel
  • 60,707
  • 10
  • 101
  • 129
  • Interesting this works. However, why this was out separate because I wanted to pass a variable down to the component. ` ` Now passing the globalStore doesn't works. – M T Aug 26 '17 at 20:00
  • 1
    @Dekel I am trying to do the exact same thing, but I am getting an error. So, I have created a new question. link: https://stackoverflow.com/questions/52652521/react-router-passing-props-to-component I am trying to access the id in console as simple as that. – Arnab Oct 04 '18 at 18:01
  • Instead of 'component' on 'Route' if you use 'render' it will give good performance – Kodali444 Nov 20 '19 at 10:21
14

In addition to Alexander Lunas answer ... If you want to add more than one argument just use:

<Route path="/details/:id/:title" component={DetailsPage}/>

export default class DetailsPage extends Component {
  render() {
    return(
      <div>
        <h2>{this.props.match.params.id}</h2>
        <h3>{this.props.match.params.title}</h3>
      </div>
    )
  }
}
Manuel
  • 2,334
  • 4
  • 20
  • 36
8

FOR version 6 ( 2022 )

Note: using useParams you can easily get your params in your component.

look at the example below

import React from "react";
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import Home from "./compo/home";
import About from "./compo/about";
import Login from "./compo/login";
import "./styles.css";
const App = () => {
  return (
    <Router>
      <div className="container">
        <Link to="/home">Home</Link>
        <Link to="/about">About</Link>
        <Link to="/login">Login</Link>
      </div>
      <Routes>
        <Route path="/home" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/login" element={<Login />} />
        <Route path="/login/:name" element={<Login />} />
      </Routes>
    </Router>
  );
};
export default App;

Login Component

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

const Login = () => {
  let { name } = useParams();

  return <h1>i am {name ? <b>{name}</b> : "login"}</h1>;
};
export default Login;

   
Kumawat Lalit
  • 410
  • 4
  • 8
6

This is for react-router-dom v6 (I highly suggest using functional components for this)

It's somewhat painful for react-router-dom to keep changing syntax and rules. But here goes nothing.

You can use both useParams and useSelector to solve this

import { useParams } from 'react-router';
import { useSelector } from 'react-redux';

const Component = () => {
const { id } = useParams();                              //returns the :id
const page = useSelector((state) => state.something[id]); //returns state of the page

return <div>Page Detail</div>;
}

export default Component;

BUT, the problem persist when you also have an action creator and you want to pass it as a props in connect function

export const connect(mapStateToProps, mapDispatchToProps)(Component)

since we are using useParams, it won't be passed to mapStateToProps that we created

const mapStateToProps = (state, ownProps) => {
console.log(ownProps)   //wont recognize :id
//hence
return {
  someReducers: state.someReducers[id] //would return an error: 'id' is not defined
 };
};

on the other hand, you can't entirely ignore the connect function since you need mapDispatchToProps to work with your component.

The workaround to this is to create a Higher Order Component withRouter function yourself. This was a deprecated react-router-dom helper.

//make this
import { useParams, useLocation, useNavigate } from 'react-router';
import { connect } from 'react-redux';
import { yourActionCreator } from '../actionCreator';

const withRouter = (Child) => {
return (props) => {
 const location = useLocation();
 const navigation = useNavigate();
 const params = useParams();
 return (
   <Child
   {...props}
   params={params}
   navigate={navigate}
   location={location}
   />
  );
 };
};

const Component = () => {
// your component...
return <div> Page Detail </div>
};

export mapStateToProps = (state, ownProps) => {
console.log(ownProps)     // would contain the :id params
return {
//something
 }
};

const mapDispatchToProps = {
yourActionCreator
}

export withRouter(connect(mapStateToProps, mapDispatchToProps)(Component));
Joundill
  • 6,828
  • 12
  • 36
  • 50
diazlp
  • 552
  • 5
  • 9
5

Here's typescript version. works on "react-router-dom": "^4.3.1"

export const AppRouter: React.StatelessComponent = () => {
    return (
        <BrowserRouter>
            <Switch>
                <Route exact path="/problem/:problemId" render={props => <ProblemPage {...props.match.params} />} />
                <Route path="/" exact component={App} />
            </Switch>
        </BrowserRouter>
    );
};

and component

export class ProblemPage extends React.Component<ProblemRouteTokens> {

    public render(): JSX.Element {
        return <div>{this.props.problemId}</div>;
    }
}

where ProblemRouteTokens

export interface ProblemRouteTokens { problemId: string; }

GSerjo
  • 4,725
  • 1
  • 36
  • 55
4

Another solution is to use a state and lifecycle hooks in the routed component and a search statement in the to property of the <Link /> component. The search parameters can later be accessed via new URLSearchParams();

<Link 
  key={id} 
  to={{
    pathname: this.props.match.url + '/' + foo,
    search: '?foo=' + foo
  }} />

<Route path="/details/:foo" component={DetailsPage}/>

export default class DetailsPage extends Component {

    state = {
        foo: ''
    }

    componentDidMount () {
        this.parseQueryParams();
    }

    componentDidUpdate() {
        this.parseQueryParams();
    }

    parseQueryParams () {
        const query = new URLSearchParams(this.props.location.search);
        for (let param of query.entries()) {
            if (this.state.foo!== param[1]) {
                this.setState({foo: param[1]});
            }
        }
    }

      render() {
        return(
          <div>
            <h2>{this.state.foo}</h2>
          </div>
        )
      }
    }
Manuel
  • 2,334
  • 4
  • 20
  • 36
2

if you are using class component, you are most likely to use GSerjo suggestion. Pass in the params via <Route> props to your target component:

exact path="/problem/:problemId" render={props => <ProblemPage {...props.match.params} />}
Matteus Barbosa
  • 2,409
  • 20
  • 21
2

I was working on react-router-dom version 6.3.0 and above solution didn't resolve my problem. Then I use something like this and it worked:

<Route exact path='/payment-status/:userId/:orderId' element={<PaymentStatus/>}/>

And on PaymentStatus.js page I did like this:

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

export const PaymentStatus = () => {

let {userId, orderId}=useParams()

return (
    <div>
    <h2>order ID : {orderId}</h2>
    <h2>user ID : {userId}</h2>
    </div>
  )
}

It worked for me. I hope it may help someone. Thanks!

1

Simple example with Class, HoC and Router v5

package.json

"react-router-dom": "5.3.1",
"react-router": "5.3.1",
"@types/react-router-dom": "5.3.3",
// YourComponent.tsx

import React from 'react';
import { RouteComponentProps, withRouter } from 'react-router';

export interface PathParams {
    id: string;
}

export interface Props extends RouteComponentProps<PathParams> {}
export interface State {}

class YourComponent extends React.Component<Props, State> {
    constructor(props: Props) {
        super(props);
        this.state = {};

        console.log(props.match.params) // { id: 1 }
        // TypeScript completions
        console.log(props.match.params.id) // 1
    }

    render() {
        return <></>;
    }
}

export default withRouter(YourComponent);
// App.tsx

import './App.css';

import React from 'react';
import { Route, Switch, Router } from 'react-router-dom';

import YourComponent from './YourComponent';

function App(): JSX.Element {
    return (
        <Router>
            <Switch>
                <Route
                    path="/details/:id"
                    component={() => <YourComponent />}
                />
            </Switch>
        </Router>
    );
}

export default App;
Filip Seman
  • 1,252
  • 2
  • 15
  • 22
1

In the latest version of (react-router-dom@6.3.0), you can do it like this:

 <Route path="path" element={<YourComponent type="simple" />} />

Here, type is the input passed to YourComponent

manojadams
  • 2,314
  • 3
  • 26
  • 30
0

try this.

<Route exact path="/details/:id" render={(props)=>{return(
    <DetailsPage id={props.match.params.id}/>)
}} />

In details page try this...

this.props.id