How to Fix the “_this.props.history Is Undefined” Error in React App

I ran head-first into this bug while wiring up a tiny demo. One moment everything compiled in react app, the next the browser console yelled at me:

Error

TypeError: _this.props.history is undefined

If you’re seeing the same thing, I’ve laid out exactly what I did wrong, why it explodes, and how I cleaned it up.

This is My index.js File

index.js

React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
import { Router, Route, browserHistory } from 'react-router';

ReactDOM.render(
<Router history={browserHistory}>
<Route path="/" component={App} />
</Router>,
document.getElementById('root')
);

App.js

React, { Component } from 'react';
import './App.css';

class App extends Component {
constructor(props) {
super(props);
this.state = {
headerText: 'Props from Header.',
contentText: 'Props from content.'
};
}

render() {
return (
<div className="App">
<ul>
<li><a href="">Home</a></li>
<li><a href="">Home</a></li>
<li><a href="">Home</a></li>
</ul>
</div>
);
}
}

export default App;

Explain Error

What I sawWhat really happens
history shows up as undefined inside the routerI mixed react-router v3 imports (Router, browserHistory) with a newer react-router-dom v5/v6 runtime. The modern runtime never injects the history prop the way v3 did, so anything that reaches for this.props.history falls over.

Quick Band aid

npm i react-router@3

That locks the project to the old API and the error disappears. I only recommend that when you cannot touch the rest of the stack (legacy codebase, tight deadline, etc.).

The Real Fix Move to react-router-dom@latest

Most projects will be happier on the current API. Below is my “mini-site” rewrite that:

  • Uses v6 (freshest as of May 2025).
  • Swaps full-page reloads for real SPA links.
  • Adds extra pages, a code-driven redirect, and a 404 screen so you can practise.

Install

npm i react-router-dom

index.js

React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

import App from './App';
import About from './About';
import Contact from './Contact';
import NotFound from './NotFound';
import './index.css';

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);

App.js (home page + menu + programmatic nav)

{ Link, useNavigate } from 'react-router-dom';
import './App.css';

function App() {
const go = useNavigate(); // lets me push a new URL from code

return (
<div className="App">
<h1>Home page</h1>

<nav>
<Link to="/">Home</Link>{' | '}
<Link to="/about">About</Link>{' | '}
<Link to="/contact">Contact</Link>
</nav>

<button onClick={() => go('/contact')}>
Go to Contact page
</button>
</div>
);
}

export default App;

About.js

default function About() {
return (
<section>
<h1>About</h1>
<p>This is a spare page so I (and you) can practise route changes.</p>
</section>
);
}

Contact.js

default function Contact() {
return (
<section>
<h1>Contact</h1>
<p>Imagine a form or address info here.</p>
</section>
);
}

NotFound.js

default function NotFound() {
return (
<section>
<h1>404</h1>
<p>Nothing is here. Use the menu to pick a valid page.</p>
</section>
);
}

What I Gained

  • Correct wrapper<BrowserRouter> automatically shares location and friends with every child. No manual prop drilling.
  • Declarative links<Link> tags flip the route instantly without refreshing the whole page.
  • Programmatic movesuseNavigate() lets me jump after a fetch, timer, or button click.
  • Nested routes – I can drop more <Route> elements inside any page to build layouts that swap only sub-regions.
  • 404 fallback – The "*" path makes sure strangers don’t land on a blank screen.

Give it a spin:

  1. Try adding /settings and create a Settings.js component.
  2. Replace the <button> logic with a post-login redirect.
  3. Nest an “edit” view under /contact/:id and grab id with useParams().

Each change will work without the infamous history error because the right router is now steering the ship.

Final Thought

Catching this bug reminded me how easy it is to a snippet from an old blog and forget that React’s ecosystem moves fast. Aligning the library version with the API you import is half the battle. The other half is taking a few minutes to add real links, programmatic navigation, and a 404 route so future me (and any teammate) can extend the project without tripping over the same mistake.

Related blog posts