Reference - React-router Project Templates
MartinJ
Posted on March 13, 2023
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:
-
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>
);
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> <Link to="/routeb">RouteB</Link>
<Outlet />
</div>
);
}
export { Tabs }
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 }
Practical issues
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.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
Likewise, once you've configured firebase,
firebase.json
hosting will initially be pointing to"public" : "public"
. After you've built a React Router project withnpm run build
, you'll need to change this to"public": "dist"
, before deploying.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,},
})
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.
In order to obtain Google indexing for "logical pages" defined by your tabs you'll need to create
<head>
information within theirreturn()
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 yourindex.html
file.
Posted on March 13, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.