React using React Router

Hey Webflow Community,

I have developed a React app that utilizes React Router and needs to run within a Webflow site. When I tested having the app load within a page in the site the initial page comes up just fine, however when I click on links that would go to a new page with the React app, Webflow’s routing takes over and I get an error page. This also happens if you try to reload the page after it has routed to a URL within the React app.
“oofta, looks like we led you astray…
THE PAGE YOU ARE LOOKING FOR DOESN’T EXIST OR HAS BEEN MOVED.”

I believe the solution is to switch to using URL hash routes instead of standard URLs but wanted to get input if anyone has gotten this scenario working before without hash routes.

Thanks for any guidance!

Mike

I have determined that the answer to this question is yes. Since any use of BrowserRouter within the React app will trigger the Webflow servers to attempt to load a page within the site.

Hey @mipaca

I know you answered your own question already, but I did some experimentation of my own and wanted to share my findings in case its helpful:

I too have been curious about how React Router works with Webflow.
What I quickly remembered is that React Router was designed for single page apps.
It’s expecting your pages to be React components and not actual html pages that Webflow generates.

This is why you can mount an entire React app inside of a Webflow page and have the routing logic work as expected, but the second you refresh the site on a path that doesn’t actually exist in Webflow, you’ll get a PAGE NOT FOUND error.

My workaround, without relying on hash routes, was to think in the Single Page App paradigm and apply it as best as possible to Webflow. What that looks like is basically:

  • making sure that each Route in React Router actually exists on Webflow
  • mounting identical react apps in the same place on every Webflow page using the routing logic

This is absolutely a hack and not recommended - just a proof of concept.
Ideally, you’d reserve Single Page Routing for Single Page Apps, and optimize your React app to work in a multi page setting like Webflow.

Here’s my demo link to play around with it:
https://test-site-react.webflow.io/

If you want to rapidly prototype with this on your site, here’s the code:

<!-- Load React, ReactDOM -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<!-- Load Babel (test purposes only) -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<!-- Load history -->
<script src="https://unpkg.com/history@5/umd/history.development.js" crossorigin></script>
<!-- Load React Router and React Router DOM -->
<script src="https://unpkg.com/react-router@6/umd/react-router.development.js" crossorigin></script>
<script src="https://unpkg.com/react-router-dom@6/umd/react-router-dom.development.js" crossorigin></script>
<script type="text/babel" data-type="module">
var BrowserRouter = ReactRouterDOM.BrowserRouter;
var Routes = ReactRouterDOM.Routes;
var Route = ReactRouterDOM.Route;
var Link = ReactRouterDOM.Link;

const usePrevious = (value) => {
  const ref = React.useRef()
  React.useEffect(() => { ref.current = value })

  return ref.current
}

const useLocationChange = (action) => {
  const location = ReactRouterDOM.useLocation();
  const prevLocation = usePrevious(location)
  React.useEffect(() => { 
    action(location, prevLocation) 
  }, [location])
}

function App() {
		const [currentPath, setCurrentPath] = React.useState('')
    const [prevPath, setPrevPath] = React.useState('')
    
    useLocationChange((location, prevLocation) => { 
    console.log('changed from', prevLocation, 'to', location) 
    setCurrentPath(location?.pathname)
    setPrevPath(prevLocation?.pathname)
  })
  
  
  return (
    <div>
    <h1>React Router, Tailwind & Babel on Webflow!</h1>
      <p>Previous Path: {prevPath} || Current Path: {currentPath}</p>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="about" element={<About />} />
      </Routes>
    </div>
  );
}

function Home() {
  return (
    <>
      <main>
        <h2>Homepage!</h2>
        <p>This component renders on the '/' path.</p>
      </main>
      <nav>
        <Link to="/about">About</Link>
      </nav>
    </>
  );
}

function About() {
  return (
    <>
      <main>
        <h2>About page</h2>
        <p>
          This component should only render on the '/about' path (but not on the actual '/about' page)
        </p>
      </main>
      <nav>
        <Link to="/">Home</Link>
      </nav>
    </>
  );
}

const container = document.getElementById('app');
ReactDOM.createRoot(container).render(
  	<BrowserRouter>
 			<App />
  	</BrowserRouter>
);
</script>

Make sure to create a div with the id “app” so you can mount this properly…
Also make to either create an “/about” page or update the Routes with your own pages that you want to test