Why don't React Router URLs work when refreshing or writing manually?

Gareth D.

The problem

When using React Router for client-side routing in your single page application (SPA), you may run into the issue of URL routes not working when a page is refreshed, the URL is written manually, or when the URL is shared. This issue may be due to your browser making a GET request to your server for a route that is not handled on your server. You have client-side routing set up, but you don’t have server-side routing set up.

This issue occurs for routes that are not on the home page / route. For example, if you navigate to one of your client-side routes, such as the /contact page, and then refresh the page, your app will crash and you’ll see the following error message on your page:

Cannot GET /contact

When your app first loads, a GET request is made to your server, which gets the JavaScript code for React and React Router. Once the JavaScript is loaded in your browser, React Router takes control of routing. If you haven’t set up server-side routing, the JavaScript code can only be fetched from the / route on your server.

React Router handles route changes on the client. It does not make a request to your server on every route change. It uses the browser’s history API to change the URL for a route client-side and it renders the view for that route. This is different to server-side routing, where a GET request is made to the server on each route change. This causes a page refresh as a new page is fetched.

The solution

To fix the issue, it’s best to add some logic on your server to handle server-side routing. You can set up server routing for each route or you can re-direct all server requests to the / route index.html file. You can also set up client-side routing such that it only makes requests to the / server route.

Hash History

You can use a hash router instead of a browser router to make all client-side requests go to the same / server route. It works by appending a # to the end of your URL root. Anything after the # won’t be sent to the server. So if you send someone a link to https://www.yourwebsite/#/contact, the browser will make a GET request to https://www.yourwebsite/ and the React Router hash router will show the /contact page. The React Router docs recommend that you don’t use hash URLs. Only use hash URLs if you can’t configure your server to direct all traffic to your React Router application.

Catch-all server route

If you have control over the server that you are using, setting up a catch-all route is a good solution. You can redirect all requests to the / route index.html file. It has the same result as the hash history strategy but without the hashes in the client-side URLs. Here’s an example of a catch-all route in an Express.js app:

app.get("/*", function (req, res) { res.sendFile(path.join(__dirname, "dist/index.html"), function (err) { if (err) { res.status(500).send(err); } }); });

You may be using a hosting service that supports client-side routing and uses a serverless backend, such as Netlify. In this case, you’ll need to configure the app as a SPA so that all requests are re-directed to the / route. This setup may be part of a question asked by the hosting service during set up or you may need to change some configuration files yourself. The steps for this should be available in the hosting provider’s docs.

Setting up server-side routes for client-side routes

You can set up your server to return pages for specific routes that are important for SEO or for all routes. This adds more complexity and you may end up with duplicate code on the client and the server. If SEO is a concern, it may be best to use a React framework that has server-side rendering or static site generation such as Next.js, Remix, or Gatsby.

Further reading

If you’re looking to get a deeper understanding of how React application monitoring works, take a look at the following articles:

Get Started With Sentry

Get actionable, code-level insights to resolve React performance bottlenecks and errors.

  1. Create a free Sentry account

  2. Create a React project and note your DSN

  3. Grab the Sentry React SDK

npm install @sentry/react
  1. Configure your DSN
import React from "react"; import ReactDOM from "react-dom"; import * as Sentry from "@sentry/react"; import App from "./App"; Sentry.init({ dsn: "https://<key>@sentry.io/<project>" }); ReactDOM.render(<App />, document.getElementById("root"));

Check our documentation for the latest instructions.

Loved by over 4 million developers and more than 90,000 organizations worldwide, Sentry provides code-level observability to many of the world’s best-known companies like Disney, Peloton, Cloudflare, Eventbrite, Slack, Supercell, and Rockstar Games. Each month we process billions of exceptions from the most popular products on the internet.

Share on Twitter
Bookmark this page
Ask a questionJoin the discussion

Related Answers

A better experience for your users. An easier life for your developers.

    TwitterGitHubDribbbleLinkedinDiscord
© 2024 • Sentry is a registered Trademark
of Functional Software, Inc.