How to Use React Router for Routing and as a Framework Part 1

React Router has always been known as a popular routing library for React Applications, however, with the recent changes and improvements in React Router, we have seen a merge between React Router and Remix(a React framework) in React Router v7. These improvements allow developers to use React Router for its routing capabilities, create robust web applications and even for data-heavy projects.
This tutorial teaches you how to use React Router in its default Declarative(Library) mode and as a Framework. In the end, we will later build a basic practice project to test out what we learned about React Router's framework mode.
Getting Started
To follow along with this tutorial, you will need to have the following:
• Basic Understanding of React and Tailwind CSS
• Prior experience with any backend-as-a-service platform is preferred, but not required
• Feel free to use your preferred code editor and package manager to follow along with this tutorial. However, I’ll be using Visual Studio Code and Pnpm as the code editor and package manager for this tutorial
If you want to check out the source code for this article, visit this GitHub link, then click on this link to view and test out what we will learn in this tutorial
What is React Router
React Router is a popular library for routing and navigation in React Applications. It allows developers to create single-page applications(SPAs), where different views are rendered without full page reloads.
React Router was created on November 1st 2020, and used as a Routing library by the same developers who would later create Remix, a React Framework. But as of November 2024, the team decided to merge both React Router and Remix with the release of React Router v7, since Remix had become a thin Wrapper around React Router.

React Router introduces three new ways to use it with the release of version 7:
Declarative (Library) mode: This is the classic React Router; it wraps routes in
<BrowserRouter/>and<HashRouter/>and defines<Route/>and<Routes/>components. It is used for client-side routing and especially when you already have a server or stack in place.Data mode: This is an advanced version of the Declarative mode, which uses Advanced loaders and actions along with pending state handling. It is used for building complex SPAs where built-in data fetching, mutation and loading are needed without fully using SSR.
Framework mode: A more advanced version of the data mode, in the sense that it wraps the data mode with a Vite plugin for a full React framework. It is used in building apps that require SEO, SSR code splitting and type safety.
In this tutorial, we will focus only on the library mode and framework mode.
Setting Up React Router as a Routing Library
When using React Router as a Routing Library, we install the React Router package in a React App and import React Router’s components to set up routing in the project.
Firstly, create a new React app using Vite and install React Router into the React Project with the command below:
pnpm add react-router@latest
Note: This article assumes you have Tailwind CSS installed.
Routing System
Routes in React Router follow a flexible structure that allows you to add any route, nest routes within other routes, or even render routes dynamically.
Creating Routes
Before you can create a route in React Router, you will first need to set up the BrowserRouter and Routes component.
Paste in the following code to add the BrowserRouter declarative router and Routes components in the main.tsx file:
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
import { BrowserRouter, Routes } from "react-router";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<BrowserRouter>
<Routes></Routes>
</BrowserRouter>
</StrictMode>
);
In the code snippet, I removed the App component that comes in as starter code and replaced it with the code snippet above.
Now that we have imported the code, let’s take a look at how they work:
BrowserRouter: A declarative router in React Router uses the browsing history for client-side routing.Routes: This renders a part of theRoutecomponent that best matches the current location of the route.To be able to add a route to the
Routescomponent, import theRoutecomponent from React Router into ourmain.tsxfile:
import { BrowserRouter, Route, Routes } from "react-router";
<Route />
Then, paste it into the Routes component with the following code:
<BrowserRouter>
<Routes>
<Route />
</Routes>
</BrowserRouter>
Structure of the Route component
<Route index element={<App />} />
The Route component consists of props to add to a route, such as:
indexprop: Once added, this makes an element and route match the default URL location.pathprop: The path that is matched by the browser.elementprop: This prop adds the component that the route will navigate to once the path matches.
Paste the following code into the App component:
const App = () => {
return <div className="text-[2.4rem] font-bold p-[22px]">Home</div>;
};
export default App;
Once you run the app, React Router will immediately render the page with the index prop on it:

Adding a Route
Routes configure element props to render when a pattern matches the URL location. It has to be rendered within a routes element.
Index Route
This route renders an element when a pattern matches the default URL location, typically serving as your home page.
To add an index route to our routes component, paste in the following Route component with an element of the App component we removed earlier to our routes component:
<BrowserRouter>
<Routes>
<Route index element={<App />} />
</Routes>
</BrowserRouter>
Now, if you navigate to the default page URL http://localhost:5173. Your page should look like this:

We explored how the index route works, but a regular route is still a bit different. A route component would need to include a path and an element since your application will typically have multiple routes.
For example, create an about route with an About component as its element, then set its path to “/about” :
<Route path="/about" element={<About />} />
Then, paste the following code into the About component:
const About = () => {
return <div className="text-[2.4rem] font-bold p-[22px]">About</div>;
};
export default About;
Once you have created the About component, navigate to the “/about” path on your browser, and React Router will display the following:

Nested Routes
Just as the Route component can be used to create a route in React Router. Routes can also be nested within a Route component. To do so, open the Route component like you would for an HTML or JSX element. Then, create a new route with the path “/more“, then open the Route component with the following code:
<Route path="/more" element={<More />}></Route>
Paste the following code into the More Component:
const More = () => {
return (
<main>
<h2 className="text-[2.4rem] font-bold p-[22px]">More</h2>
</main>
);
};
export default More;
Next, create two more routes with a path of “/blogs“ and “/help“ along with their appropriate components, and wrap them within the “/more“ route:
<Route path="/more" element={<More />}>
<Route path="blogs" element={<Blogs />} />
<Route path="help" element={<Help />} />
</Route>
Paste the following code into the Blogs and Help components:
Blogs component:
const Blogs = () => {
return <div className="text-[2.4rem] font-bold p-[22px]">Blog</div>;
};
export default Blogs;
Help component:
const Help = () => {
return <div className="text-[2.4rem] font-bold p-[22px]">Help</div>;
};
export default Help;
Once you navigate to the “more” route path on your browser, you will only see the More component, because we forgot to add an important component that is used when working with nested routes in React Router, which is the Outlet component.
Outlet component: This renders a child route of a parent route if it matches, or nothing if no child route gets matched.
Import the Outlet component into our More.tsx component and paste some JSX elements into our project’s UI:
import { Outlet } from "react-router";
const More = () => {
return (
<main>
<h2 className="text-[2.4rem] font-bold p-[22px]">More</h2>
<Outlet />
</main>
);
};
export default More;
Now, if you try to navigate to the “more“ route, then the “help” and ”blogs” route that are nested in it, the browser should display the following:

Layout Routes
Layouts are pretty similar to nested routes in the sense that both make use of the Outlet component and require other routes to be nested within them. Although the Layout route doesn't have a specific route name, it uses the route file path to nest other child routes.
It is used to render routes in a React app when you need to keep a specific part of the UI accessible on every page. For example, a navbar is displayed on every page for easier navigation.
To try that out, create a new route with the path "/" and a Layout component as the element using the following code:
const Layout = () => {
return (
<main>
<nav className="h-[60px] w-full bg-[#111] flex justify-between items-center py-[8px] px-[12px]">
<h2 className="text-[2.2rem] font-bold text-indigo-500">RRV7</h2>
</nav>
</main>
);
};
export default Layout;
We then add the Outlet component so that it will display the route we navigate to while also having the UI elements we created before it:
import { Outlet } from "react-router";
const Layout = () => {
return (
<main className="h-fit w-full flex flex-col">
<nav className="h-[60px] w-full bg-[#111] flex justify-between items-center py-[8px] px-[12px]">
<h2 className="text-[2.2rem] font-bold text-indigo-500">RRV7</h2>
</nav>
<Outlet />
</main>
);
};
export default Layout;
Before we check out the Layout route in action, open the Layout route and nest all the other routes we created previously within it:
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<App />} />
<Route path="/about" element={<About />} />
<Route path="/more" element={<More />}>
<Route path="blogs" element={<Blogs />} />
<Route path="help" element={<Help />} />
</Route>
</Route>
</Routes>
</BrowserRouter>
Now, if you navigate to any route, you should see the navbar at the top:

Dynamic Routes
As the name suggests, dynamic routes are routes that are displayed dynamically. This means the content initially on the page will serve as a template for all the routes that will be shown. Dynamic Routes are typically used to render pages for multiple routes, often using API data.
To add a dynamic route to our React app, we need to change the blog route path inside the blogs route, which is nested in the more route, to “blog/:id“:
<Route path="blogs/:id" element={<Blogs />} />
The “/:id” section of the path is included to represent any route that follows after the “blogs/” section of the path.
Now, in the Blogs component we created earlier, import the useParams() hook from React Router:
import { useParams } from "react-router";
Then, create a destructured variable of id from the new instance of the useParams hook:
const { id } = useParams();
Now, render the id variable in the JSX element of the Blogs component with the following code:
import { useParams } from "react-router";
const Blogs = () => {
const { id } = useParams();
return <div className="text-[2.4rem] font-bold p-[22px]">Blog:{id}</div>;
};
export default Blogs;
Once you navigate to the “/more/blogs” , if you add anything else to the path, like “1” after the “/”, the useParams() hook will return the value “1” where you rendered the variable id:

So, if you are working with an API, you can use the ID obtained from the blog route to fetch data from the API. This allows you to retrieve the item by its ID while keeping the initial UI intact.
Links and Navigation
We have examined routes and explored them using the browser. However, in a React application, you need to move through the page routes using links. Adding links in React Router is very similar to adding links in HTML with the anchor tag <a>. But in React Router, we use the Link component.
To create a new link, go back to the navbar we created in the Layout component we made earlier, and paste the following code:
The Link component: This is an enhanced version of the <a> tag to enhance client-side routing.
The NavLink component: This is an enhanced version of the <a> tag and Link component. It is configured to be used for links in menus because it has an isActive A boolean property that is set to true if it is the clicked link. This can be useful if you want to show the route that is currently open on a menu or navbar.
Now that we know what a Link component is, let's add some NavLink components to enable routing to the routes we created using a navbar:
import { NavLink, Outlet } from "react-router";
<nav className="h-[60px] w-full bg-[#111] flex justify-between items-center py-[10px] px-[16px]">
<h2 className="text-[2.2rem] font-bold text-indigo-500">
<NavLink to="/">RRV7</NavLink>
</h2>
<ul className="flex items-center gap-x-[26px] [&>a]:text-[1.3rem] [&>a]:font-bold [&>a]:text-[#f1f1f1] pr-[8px]">
<NavLink to="/about">About</NavLink>
<NavLink to="/more">More</NavLink>
</ul>
</nav>
Once you click on a link, you should be sent to the appropriate routes:

Click on this link to view the source code.
Using React Router as a Framework
So far in this article, we have learned how to add routing to a React application. Now, we will focus on building web applications using React Router. We will also use many of the hooks and components we explored earlier in library mode.
Setting Up a React Router Project
Starting a new React Router app is quite easy due to the way the installation is set up using Vite or React Router’s standard installation method. To begin a new React Router project using Vite and select React Router V7 inside the React section. Then, install the following dependencies:
pnpm create vite@latest my-react-router-app
The code snippet above is the way to start a new React Router v7 project using Vite. However, if you want to install React Router v7 manually, run the following command:
npx create-react-router@latest my-react-router-app
Now, navigate to the my-react-router-app/ directory, and start the app:
cd my-react-router-app
npm install
npm run dev
It is similar to using Vite with the Pnpm package manager. Run the following commands to navigate to the project folder and start the app:
cd my-react-router-app
pnpm install
pnpm run dev
Creating Your First React Router App
Like every other JavaScript or React Library and Framework, React Router comes with starter code. Once you run the app and navigate to localhost:5173 on your browser. Your page should display this:

Understanding The Project Structure

The folder and file structure is quite different from React or any other React framework. In React Router, the following folders and files are added when you start up a new React Router app:
• app/ folder: This folder is similar to the src folder, in the sense that it stores all the core folders and files of your project, such as:
• routes/ folder: This folder stores all the pages or routes used in the project.
• home.tsx file: The home.tsx file is located inside the routes folder. It is created when a new project is initialized and is set as the index route module of the project.
• welcome.tsx file: This file is located inside the welcome/ folder; it is a component rendered in the home route.
• app.css file: This file adds styling to the routes and components. It can be used to apply global styles. However, the latest version of Tailwind is installed by default when you create a new React Router App.
• routes.ts file: The routes.ts file adds routing and navigation to our project. By default, the routes.ts file has an index route pointing to the home.tsx route.
• Dockerfile file: This is used to build a Docker image.
• .dockerignore file: Similar to .gitignore, The .dockerignore file tells Docker which files/folders to exclude from the Docker build context.
• react-router.config.ts file: This config file is specific to React Router; it controls how your project behaves during development and processes.
Pages and Routing
Pages and routing in React Router use many built-in components and route wrappers from React Router that are also used in the library mode. We will use some of these as we move forward in this tutorial.
Structure of a Route
export default [index("routes/home.tsx")] satisfies RouteConfig;
React Router uses a different approach to routing compared to the React Router library mode. There are still routes like index, regular routes, and dynamic routes, but in framework mode, the routes are in lowercase and are stored in an array within the routes.ts file and are exported.
The route above in the code snippet is an index route. To add routing to our code, import the index route from React Router and paste the following code into the routes.ts file:
import { type RouteConfig, index } from "@react-router/dev/routes";
export default [index("routes/home.tsx")] satisfies RouteConfig;
For other routes, import the route component:
• index component: The parentheses in the index component accept the route file path, which in this case is the home route:
import { type RouteConfig, index, route } from "@react-router/dev/routes";
export default [index("routes/home.tsx")] satisfies RouteConfig;
• route component: The parentheses in the route component consist of the route path, route file path, and an optional children array in case you want to nest routes:
import { type RouteConfig, index, route } from "@react-router/dev/routes";
export default [index("routes/home.tsx"), route("about", "routes/about.tsx")] satisfies RouteConfig;
Note: Files referenced in the routes.ts file are called route modules
Remove the welcome components and change the home.tsx route module from the starter code that is generated once you create a new React Router App to the following:
import type { Route } from "./+types/home";
export function meta({}: Route.MetaArgs) {
return [
{ title: "New React Router App" },
{ name: "description", content: "Welcome to React Router!" },
];
}
export default function Home() {
return <main className="text-[2.4rem] font-bold p-[22px]">Home</main>;
}
The meta function in this code snippet is used to add meta tags like title, name and content to the page, for example, the title “New React Router App“ will be the title of the page :

Once you reload your browser, the browser will display the home route module with the changes:

Creating a Route and Route module
We just glanced through this in the structure of a route section, to use a regular route in our code, create a new route with a path of “about“ and an about route module, then paste in the following code into the about route module:
import {
type RouteConfig,
index,
route,
} from "@react-router/dev/routes";
route("about", "routes/about.tsx")
const about = () => {
return <main className="text-[2.4rem] font-bold p-[22px]">About</main>;
};
export default about;
Once you navigate to “localhost:5173/about“ on your browser, the page will display the following:

Layout Routes
Layout routes are used to nest child routes and display the nested routes while keeping the main structure of the parent route's UI visible. This is similar to what we did earlier in library mode when we learned about layout routes, but with some minor differences. They work without adding a route name, just the route file path, and can be added at different levels of your app.
To make use of a layout route in our code, import a layout route from React Router and edit the routes.ts file to have a layout route with the following code:
import {
type RouteConfig,
index,
layout,
route,
} from "@react-router/dev/routes";
export default [
index("routes/home.tsx"),
layout("routes/layout.tsx", [route("/about", "routes/about.tsx")]),
] satisfies RouteConfig;
I included only the “about“ route in the layout route, so that we can see how a layout route module with a navbar works.
Now, create a layout route module and paste the following code:
import { NavLink, Outlet } from "react-router";
const layout = () => {
return (
<main className="h-fit w-full flex flex-col">
<nav className="h-[60px] w-full bg-[#111] flex justify-between items-center py-[10px] px-[16px]">
<h2 className="text-[2.2rem] font-bold text-indigo-500">RRV7</h2>
</nav>
<Outlet />
</main>
);
};
export default layout;
In this code snippet, I imported the Outlet component from React Router, which we used when working with React Router's library mode, because now we need to display routes nested within the layout route.
The JSX element we added in the code is for the navbar and will be displayed on any route nested within the layout route .
Now, when you return to the home route, everything should work the same way. However, any JSX elements in the layout route module will appear on all routes within the layout route, such as the about route, we later nested within the layout route:

Nested Routes
You saw earlier in the route component that the optional children array can be used to nest routes. Create a new route with a path of “/info“ in our routes.ts file along with two other routes, with paths of “contact” and “team“ routes as routes nested within it:
route("/info", "routes/info.tsx", [
route("contact", "routes/contact.tsx"),
route("team", "routes/team.tsx"),
]),
Next, create route modules for the info, contact and team route and paste the following code:
inforoute module:
import { Outlet } from "react-router";
const info = () => {
return (
<main>
<h2 className="text-[2.4rem] font-bold p-[22px]">Info</h2>
<Outlet />
</main>
);
};
export default info;
The info route module also makes use of an Outlet component, so that when we navigate to the “contact” or “team” route path, it will still show JSX elements from the info route component.
contactroute module:
const contact = () => {
return <main className="text-[2.4rem] font-bold p-[22px]">Contact</main>;
};
export default contact;
teamroute module:
const team = () => {
return (
<main className="text-[2.4rem] font-bold p-[22px]">Team</main>
)
}
export default team
Once you have created all the route modules and components, open your web browser to view them. Your browser will display the following:

Dynamic Segment
In React Router, dynamic segments are parsed from the URL and provided as params to other router APIs when the route matches the URL. They are useful when working with a list of items returned from an API or Database. If a route path segment starts with : then it becomes a dynamic segment.
Paste the following route into the route.ts file
route("blogs/:id", "routes/blogs.tsx")
To access a dynamic segment, in the blogs route module, create a loader function and paste in the following code:
export const loader = ({ params }: Route.LoaderArgs) => {
const id = params.id;
return { id };
};
We add the parameters of params to the loader function along with its appropriate type annotation:
import type { Route } from "./+types/blogs";
export const loader = ({ params }: Route.LoaderArgs) => {
const id = params.id;
return { id };
};
const blogs = ({ loaderData }: Route.ComponentProps) => {
return (
<main className="text-[2.4rem] font-bold p-[22px]">
Blogs: {loaderData.id}
</main>
);
};
export default blogs;
Then we return the ID. When you access it with the route module, it appears in the part of the JSX element where you specified the data obtained from the loaderData parameter in our route module.
Once you navigate to the “blogs/:id” route module with the segment set to “1”, your page will be displayed with “1” in it:

Route Prefixes
With Route Prefixes, you can add prefixes to a group of routes without needing a parent route file.
To use a prefix route in this project, change the nested route module to a prefix route module in the routes.ts file:
...prefix("/info", [
route("contact", "routes/contact.tsx"),
route("team", "routes/team.tsx"),
]),
Explore the route modules on your web browser to see how they function compared to nested routes:

You won’t need to use an Outlet component since all you're using is the route path, it will only display the child routes in your browser.
Once you try to navigate to the “/info“ route with a prefix, React Router returns a “404“ error since the route file path can’t be found:

Links and Navigation
Now that we have created some regular, nested, prefix, and dynamic routes, update the routes.ts file with the following to nest all the routes within the layout route so it will be easier to navigate to all the routes with the navbar:
import {
type RouteConfig,
index,
layout,
prefix,
route,
} from "@react-router/dev/routes";
export default [
layout("routes/layout.tsx", [
index("routes/home.tsx"),
route("/about", "routes/about.tsx"),
...prefix("/info", [
route("contact", "routes/contact.tsx"),
route("team", "routes/team.tsx"),
]),
]),
] satisfies RouteConfig;
In the layout route module, paste in the following code to update the navbar, adding links to the navbar with the NavLink and Link component:
import { NavLink, Outlet } from "react-router";
const layout = () => {
return (
<main className="h-fit w-full flex flex-col">
<nav className="h-[60px] w-full bg-[#111] flex justify-between items-center py-[10px] px-[16px]">
<h2 className="text-[2.2rem] font-bold text-indigo-500">
<NavLink to="/">RRV7</NavLink>
</h2>
<ul className="flex items-center gap-x-[26px] [&>a]:text-[1.3rem] [&>a]:font-bold [&>a]:text-[#f1f1f1] pr-[8px]">
<NavLink to="/about">About</NavLink>
<NavLink to="/info/contact">Contact</NavLink>
<NavLink to="/info/team">Team</NavLink>
</ul>
</nav>
<Outlet />
</main>
);
};
export default layout;
If you remember clearly, we used only the NavLink component when we explored Links and Navigation with React Router’s library mode. The Link component works similarly.
Now, try clicking the buttons on the navbar to navigate to all the route modules. Each button should take you to the specified route:

Click on this link to view the links and navigation, or try it yourself.
Components
Components in React Router are similar to those in plain React. Components can be imported into any route in the project.
Create a new component with any name of your choice outside the routes folder, and import it into the home.tsx file, it will work similarly to how it does in plain React. Just like the welcome component we removed earlier, since it was starter code.
Adding Styling with CSS and Tailwind CSS
In React Router, you can add global styles with CSS or choose to use a CSS framework like Tailwind. The latest version of Tailwind CSS is installed by default when you create a new React Router App. We will be using Tailwind CSS throughout this section of the tutorial, just as we did for React Router’s library mode.
Next Steps
This wraps up the first part of the tutorial. In this section, we explored React Router V7, its purpose, use cases, the changes it introduced, and how to use React Router in both its library and framework modes.
We also learned about many other concepts, such as the different types of routing supported in both modes of React Router, as well as how links and navigations are implemented.
Now that we are done with this part of the article, click on this link to take you to the second part. Thank you for reading!



