reactjsreact-router-dom

How to build truly "folder agnostic" react apps using yarn and react-dom-router?


I need to build a small react application (I'm using yarn and react-dom-router) to be truly "folder agnostic". That means that I don't know the folder of the web server where that app will be deployed.

It could be

/foo/
/bar/
/foo/bar/
/whatever/foo/bar/

So I followed some internet wisdom and added to package.json

"homepage": "./"

And I also changed this in vite.config.ts

base: "./"

But now I'm not sure on how to set the router. I tried several different options for the initial path, but none of them work:

import { createBrowserRouter } from "react-router-dom";
import { HomeView } from "./views/home";
import { SceneryView } from "./views/scenery";
import { NotFoundView } from "./views/not-found";
import { DefaultLayout } from "./layouts/default-layout";
import { PatientView } from "./views/patient";
import { PatientEndView } from "./views/patient-end";
import { CompletedView } from "./views/completed";

const router = createBrowserRouter([
  {
    path: "/", // I think the problem is there
    element: <DefaultLayout />,
    children: [
      {
        index: true,
        element: <HomeView />,
      },
      {
        path: "decision-pathways/scenery/:id",
        element: <SceneryView />,
      },
      {
        path: "decision-pathways/patient/:sceneryId/:patientId",
        element: <PatientView />,
      },
      {
        path: "decision-pathways/patient/end/:sceneryId/:patientId",
        element: <PatientEndView />,
      },
      {
        path: "decision-pathways/completed",
        element: <CompletedView />,
      },
    ],
  },
  {
    path: "*",
    element: <NotFoundView />,
  },
]);

export default router;

For the "main" path I tried:

.
./
/

But none of them worked, I always get the not found page. Anyone tackled the same problem?


Solution

  • My problem was that I was stubbornly trying to implement createBrowserRouter that is essentially not compatible with my scenario.

    Thanks to the comment by @DrewReese I switched to createHashRouter

    import { createHashRouter } from "react-router-dom";
    import { HomeView } from "./views/home";
    import { SceneryView } from "./views/scenery";
    import { NotFoundView } from "./views/not-found";
    import { DefaultLayout } from "./layouts/default-layout";
    import { PatientView } from "./views/patient";
    import { PatientEndView } from "./views/patient-end";
    import { CompletedView } from "./views/completed";
    
    const router = createHashRouter([
      {
        path: "/",
        element: <DefaultLayout />,
        children: [
          {
            index: true,
            element: <HomeView />,
          },
          {
            path: "decision-pathways/scenery/:id",
            element: <SceneryView />,
          },
          {
            path: "decision-pathways/patient/:sceneryId/:patientId",
            element: <PatientView />,
          },
          {
            path: "decision-pathways/patient/end/:sceneryId/:patientId",
            element: <PatientEndView />,
          },
          {
            path: "decision-pathways/completed",
            element: <CompletedView />,
          },
        ],
      },
      {
        path: "*",
        element: <NotFoundView />,
      },
    ]);
    
    export default router;
    

    What's the difference? Basically createHashRouter add an hashtag before react "internal" route. In this way the web server ignores whatever is after that hashtag and resolves always to the same file, in this case my index.html.

    For the resources all that is needed to change the paths to relative ones is this line in vite.config.js:

    base: "./"