DEV Community

MartinJ
MartinJ

Posted on • Edited on

Reference - React-router Project Templates

Last Reviewed: September 2023

Introduction

If you use React in your webapps, it's quite likely that you will end up using React-router as well. This is because even the simplest single-page https://mywebsite.com webapp will likely find it useful to register an https://mywebsite.com/management secured page in order to maintain its configuration data.

So, if all your React projects are going to be based on React-router designs, it seems that it would be a good idea if they share common folder structure and file-naming conventions specifically suited to a React-router single-page webapp.

Here's what I've been using for both folder structure and file-naming (acknowledgments here go to Faraz Ahmad), together with a sample code template for a popular react-router use-case

Folder structure and File-naming conventions

A structure I've been using successfully for a while on my Vite React Router projects now goes like this:

Folder structure

  • assets - graphic files etc
  • components - components that are shared around the webapp
  • lib - javascript functions and constants that are likewise shared around the webapp
  • routes - "master" components defining the webapps routes. Sub-components local to a master component are declared within that component
  • styles - shared stylesheets

Hosting your fixed assets in the public folder as shown above makes for simplicity but means that they aren't built into the webapp's "bundle". This suits me but your case may be different. See Where to Store Images in React App for a useful discussion of this issue.

Because component functions are commonly signaled by capitalisation of the first character of their name, I've found it best to follow the same convention for naming source file names as well. Where sub-components are only referenced by their parent, I find it reduces clutter if I put their code within their parent's file.

Note that, if you plan to use Vite as your local development server, component files containing JSX are expected to use .jsx filename extensions.

If you have a deep interest in project structures, you might find it useful to have a look at Configuring a Firebase/React webapp for life post-implementation. This describes the problems you may encounter when trying to test enhancements to a system that is already in operational use. The solution proposed here works well for me and may help you too.

Code template for a "persistent tabs" webapp

A layout you'll see time and time again in website layouts is one that uses some sort of persistent "menu" to control the "page" content that populates the rest of the available screen space.

Typically this arrangement will take the form of a menu bar with clickable "tabs". These determine the page displayed beneath the bar.

React-router is great for delivering this sort of design. Here's code for a Vite main.jsx file delivering a persistent Tabs.jsx menu component controlling RouteA.jsx and RouteB.jsx page components. The persistence of the Tabs component is achieved by adding a React <Outlet /> tag to the Tabs component

Note an interesting feature of the sample code. Generally, one of your route pages will be designated a Home page - ie the page that is displayed by default when the webappp starts. In order to get this page rendered both when the webapp is called with the root url as well as when the page is called with its own root/.. url, the trick is to make this page an index Route. This means that it gets rendered when none of the other Routes match the url.

Here's my main.jsx template. In this case, the RouteA component has been selected to be the "indexed" Home page:

import React from "react";
import ReactDOM from "react-dom/client";
import { createBrowserRouter, createRoutesFromElements, RouterProvider, } from "react-router-dom";
import { Route } from "react-router-dom";

import { Tabs } from './routes/Tabs';
import { RouteA } from './routes/RouteA';
import { RouteB } from './routes/RouteB';

const router = createBrowserRouter(
    createRoutesFromElements(
            <Route path="" element={<Tabs />} >
                <Route path="/routea" element={<RouteA />} />
                <Route path="/routeb" element={<RouteB />} />

                <Route index element={<RouteA />} />

            </Route>
    )
)

ReactDOM.createRoot(document.getElementById("root")).render(
    <React.StrictMode>
        <RouterProvider router={router} />
    </React.StrictMode>
);


Enter fullscreen mode Exit fullscreen mode

Here's the Tabs.js component that delivers the "menu" bar. This lays out a header menu bar that uses React-router's basic Link component to activate a route change. You can add an onClick function to the component tag's invocation if you want to do something fancy like setting state when the tab is clicked. You might also consider replacing <Link> by <NavLink> if you'd like the tab's formatting to reflect its "clicked" state.

import { Link, Outlet } from "react-router-dom";

function Tabs() {

    return (
        <div>
            <Link to="/routea">RouteA</Link>&nbsp;&nbsp;<Link to="/routeb">RouteB</Link>
            <Outlet />
        </div>
    );
}
export { Tabs }
Enter fullscreen mode Exit fullscreen mode

And finally, here's my RouteA.js. This displays content for page A beneath the persistent Tabs menu bar. Further pages, obviously, will be just clones of this.

function RouteA() {
    return (<h2>RouteA output</h2>);
}

export { RouteA }
Enter fullscreen mode Exit fullscreen mode

Practical issues

  1. While the example shown above uses JSX to configure the webapp's routes in src/main.jsx, this specification could just as easily have been suppled as a javascript object. Personally I find JSX more natural.

  2. It seems (as of March 2023) that "out of the box", Vite's scaffolding of your project won't configure a linter. This means you won't know about code issues until they actually cause a problem. For instructions on how to fix this (at the expense of some reduction in the speed advantages of Vite over React), see Vite with ESLint

  3. Likewise, once you've configured firebase, firebase.json hosting will initially be pointing to "public" : "public". After you've built a React Router project with npm run build, you'll need to change this to "public": "dist", before deploying.

  4. To enable debugging in the browser for webapps built using Vite, configure the vite.config.js file in a project's root as follows:

export default defineConfig({
    plugins: [react()],
    build: {sourcemap: true,},
})
Enter fullscreen mode Exit fullscreen mode
  1. Complete the setup by adding your project folder to the Inspector's workspace.You'll find your source under the new entry that will appear here once you've approved use.

  2. In order to obtain Google indexing for "logical pages" defined by your tabs you'll need to create <head> information within their return() code. See Highs and Lows of a Firebase/React Development for advice on this. Also note that, by default, the indexing of your root page will be guided by the <head> settings in your index.html file.

Top comments (0)