Routing – React Router
Yes. If you're writing this as a current React Router blog, I would update the previous content to use the Data Router approach with createBrowserRouter + RouterProvider.
The current React Router docs show createBrowserRouter() with route objects, and RouterProvider renders the router. The current installation docs also use react-router rather than the older react-router-dom-centric setup. (React Router)
React Router with createBrowserRouter
React applications often contain multiple pages:
/
├── /products
├── /products/:productId
├── /cart
├── /login
└── /dashboard
Instead of loading a completely new HTML page for every URL, React Router allows us to manage navigation on the client side.
The modern approach is to create a router using:
createBrowserRouter()
and provide it to React using:
<RouterProvider />
The basic flow is:
URL
↓
createBrowserRouter
↓
Match Route
↓
Load Route Data
↓
Render Component
1. Installing React Router
For a new application using the current React Router Data mode:
npm install react-router
The official installation documentation currently uses react-router. (React Router)
Then we can import:
import {
createBrowserRouter,
} from "react-router";
import {
RouterProvider,
} from "react-router/dom";
For browser applications, the current documentation recommends RouterProvider from react-router/dom, which wires up the React DOM flushSync implementation automatically. (React Router)
2. What is createBrowserRouter?
createBrowserRouter() creates a data router for the application.
For example:
const router = createBrowserRouter([
{
path: "/",
Component: Home,
},
]);
The argument is an array of route objects.
A route object describes things such as:
path
Component
children
loader
action
errorElement
lazy
and more. (React Router)
So instead of defining routes like this:
<Routes>
<Route path="/" element={<Home />} />
</Routes>
we can define them as JavaScript objects:
const router = createBrowserRouter([
{
path: "/",
Component: Home,
},
]);
3. RouterProvider
After creating the router, we need to give it to React.
<RouterProvider router={router} />
For example:
import ReactDOM from "react-dom/client";
import {
createBrowserRouter,
} from "react-router";
import {
RouterProvider,
} from "react-router/dom";
const router = createBrowserRouter([
{
path: "/",
Component: Home,
},
]);
ReactDOM.createRoot(
document.getElementById("root")
).render(
<RouterProvider router={router} />
);
RouterProvider renders the UI for the router and should generally be placed near the top of the application tree. The router itself should be created outside the React render tree rather than recreated during renders. (React Router)
4. Basic Routing
Let's create a few components:
const Home = () => {
return <h1>Home Page</h1>;
};
const Products = () => {
return <h1>Products Page</h1>;
};
const Cart = () => {
return <h1>Cart Page</h1>;
};
Now define our routes:
const router = createBrowserRouter([
{
path: "/",
Component: Home,
},
{
path: "/products",
Component: Products,
},
{
path: "/cart",
Component: Cart,
},
]);
Now React Router knows:
/ → Home
/products → Products
/cart → Cart
The routing flow becomes:
User visits /products
↓
Router matches /products
↓
Products Component
↓
React renders Products UI
5. Route Objects
The objects passed to createBrowserRouter() are called Route Objects.
For example:
{
path: "/products",
Component: Products,
}
A route object can contain much more than just a path and component.
For example:
{
path: "/products/:id",
Component: ProductDetails,
loader: productLoader,
errorElement: <ErrorPage />,
}
This is one of the major advantages of the Data Router approach.
The route configuration can contain:
Route
├── path
├── Component
├── loader
├── action
├── errorElement
├── children
└── lazy
React Router's current Route Object documentation describes route objects as the foundation for data loading, actions, revalidation, error boundaries, lazy loading, and other route behavior. (React Router)
6. Link
For navigation between routes, we can use Link.
import { Link } from "react-router";
const Navbar = () => {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/products">
Products
</Link>
<Link to="/cart">
Cart
</Link>
</nav>
);
};
The important difference is that we don't need to manually manipulate:
window.location
React Router handles the client-side navigation.
Conceptually:
Click Link
↓
Router navigation
↓
URL changes
↓
Route matching
↓
New component renders
7. Link vs <a>
For internal React Router navigation:
<Link to="/products">
Products
</Link>
is generally preferred over:
<a href="/products">
Products
</a>
The Link component participates in React Router's client-side navigation system.
8. Dynamic Routes
A very common requirement is displaying a product based on its ID.
For example:
/products/1
/products/2
/products/100
We don't want to create:
/products/1
/products/2
/products/3
...
Instead, we define a dynamic segment:
const router = createBrowserRouter([
{
path: "/products/:productId",
Component: ProductDetails,
},
]);
The :productId part is a dynamic segment.
React Router parses dynamic segments and makes them available to the route through routing APIs. (React Router)
9. useParams
Inside the component, we can retrieve the parameter using useParams.
import { useParams } from "react-router";
const ProductDetails = () => {
const { productId } = useParams();
return (
<h1>
Product ID: {productId}
</h1>
);
};
For:
/products/101
we get:
productId = "101"
For:
/products/500
we get:
productId = "500"
So:
/products/:productId
↓
useParams()
↓
productId
10. Nested Routes
Nested routing is extremely useful for dashboard applications.
Suppose we have:
/dashboard
/dashboard/profile
/dashboard/settings
/dashboard/orders
We can configure:
const router = createBrowserRouter([
{
path: "/dashboard",
Component: Dashboard,
children: [
{
index: true,
Component: DashboardHome,
},
{
path: "profile",
Component: Profile,
},
{
path: "settings",
Component: Settings,
},
{
path: "orders",
Component: Orders,
},
],
},
]);
Notice that the child paths don't start with /.
We write:
path: "profile"
rather than:
path: "/dashboard/profile"
React Router combines the parent and child paths.
So:
/dashboard
+
profile
↓
/dashboard/profile
Nested routes are configured through the children property. (React Router)
11. Outlet
Now we need to decide where the child route should render.
That's where Outlet comes in.
import { Outlet } from "react-router";
const Dashboard = () => {
return (
<div>
<h1>Dashboard</h1>
<nav>
<Link to="profile">
Profile
</Link>
<Link to="settings">
Settings
</Link>
</nav>
<Outlet />
</div>
);
};
Suppose the URL is:
/dashboard/profile
The rendering becomes:
Dashboard
│
├── Dashboard UI
│
└── <Outlet />
│
↓
Profile
If the URL changes to:
/dashboard/settings
then:
Dashboard
│
├── Dashboard UI
│
└── <Outlet />
│
↓
Settings
React Router renders child routes through the parent's <Outlet />. (React Router)
12. Index Routes
An index route is the default child route.
For example:
{
path: "/dashboard",
Component: Dashboard,
children: [
{
index: true,
Component: DashboardHome,
},
{
path: "settings",
Component: Settings,
},
],
}
Now:
/dashboard
↓
DashboardHome
/dashboard/settings
↓
Settings
The important part is:
index: true
An index route renders into the parent's Outlet at the parent's URL. (React Router)
13. Programmatic Navigation
Sometimes navigation happens after some logic.
For example:
Login
↓
API request
↓
Login successful
↓
Dashboard
We can use:
useNavigate()
Example:
import { useNavigate } from "react-router";
const Login = () => {
const navigate = useNavigate();
const handleLogin = async () => {
// login API
navigate("/dashboard");
};
return (
<button
Login
</button>
);
};
The flow is:
Login Button
↓
API Request
↓
Success
↓
navigate("/dashboard")
↓
Dashboard
14. Navigate Back
useNavigate() can also work with browser history.
navigate(-1);
means:
Go back
For example:
<button => navigate(-1)}>
Back
</button>
We can also use:
navigate(1);
to move forward in the browser history.
15. Protected Routes
Authentication is a common React Router requirement.
Suppose:
/dashboard
should only be accessible to authenticated users.
With the Data Router approach, we can use route-level loaders and redirects for authentication.
For example:
import {
redirect,
} from "react-router";
const dashboardLoader = async () => {
const isAuthenticated = checkAuth();
if (!isAuthenticated) {
throw redirect("/login");
}
return null;
};
Then:
const router = createBrowserRouter([
{
path: "/dashboard",
Component: Dashboard,
loader: dashboardLoader,
},
]);
The flow becomes:
User visits /dashboard
↓
dashboardLoader
↓
Authenticated?
/ \
Yes No
↓ ↓
Dashboard redirect
↓
/login
This is one of the areas where the Data Router approach becomes particularly powerful: route configuration can contain data-loading and navigation behavior rather than putting all routing logic inside components. (React Router)
16. Route Loaders
One of the biggest advantages of createBrowserRouter is the loader.
Suppose we want to load a product before rendering its page.
We can write:
const productLoader = async ({ params }) => {
const response = await fetch(
`/api/products/${params.productId}`
);
return response.json();
};
Then:
const router = createBrowserRouter([
{
path: "/products/:productId",
Component: ProductDetails,
loader: productLoader,
},
]);
Now the route itself knows:
Route
├── URL
├── Component
└── Data loader
17. useLoaderData
Inside the component:
import { useLoaderData } from "react-router";
const ProductDetails = () => {
const product = useLoaderData();
return (
<div>
<h1>{product.name}</h1>
<p>${product.price}</p>
</div>
);
};
The flow becomes:
Navigate to /products/10
↓
Route matches
↓
loader()
↓
Fetch product
↓
Loader data
↓
ProductDetails
↓
useLoaderData()
↓
Render product
React Router's Data Router APIs allow loaders to load data associated with a route. (React Router)
18. Why createBrowserRouter Is Powerful
The older mental model was mostly:
URL
↓
Route
↓
Component
With createBrowserRouter, a route can describe much more:
Route
│
┌────────────┼────────────┐
↓ ↓ ↓
URL Component Loader
│
↓
Data
And it can additionally support things like:
actions
error boundaries
lazy loading
revalidation
middleware
depending on the routing setup. (React Router)
19. Complete Modern Example
Here's a good starting structure for a blog or interview project:
import ReactDOM from "react-dom/client";
import {
createBrowserRouter,
} from "react-router";
import {
RouterProvider,
} from "react-router/dom";
import {
Link,
Outlet,
useParams,
useNavigate,
} from "react-router";
const Home = () => {
return (
<div>
<h1>Home</h1>
<Link to="/products">
Products
</Link>
</div>
);
};
const Products = () => {
return <h1>Products</h1>;
};
const ProductDetails = () => {
const { productId } = useParams();
return (
<h1>
Product: {productId}
</h1>
);
};
const Dashboard = () => {
return (
<div>
<h1>Dashboard</h1>
<Link to="profile">
Profile
</Link>
<Link to="settings">
Settings
</Link>
<Outlet />
</div>
);
};
const Profile = () => {
return <h2>Profile</h2>;
};
const Settings = () => {
return <h2>Settings</h2>;
};
const NotFound = () => {
return <h1>404 - Page Not Found</h1>;
};
const router = createBrowserRouter([
{
path: "/",
Component: Home,
},
{
path: "/products",
Component: Products,
},
{
path: "/products/:productId",
Component: ProductDetails,
},
{
path: "/dashboard",
Component: Dashboard,
children: [
{
index: true,
Component: Profile,
},
{
path: "profile",
Component: Profile,
},
{
path: "settings",
Component: Settings,
},
],
},
{
path: "*",
Component: NotFound,
},
]);
ReactDOM.createRoot(
document.getElementById("root")
).render(
<RouterProvider router={router} />
);
20. Modern React Router Architecture
The overall architecture is now:
createBrowserRouter()
│
↓
Route Objects
│
┌───────────────┼───────────────┐
↓ ↓ ↓
path Component loader
│ │ │
↓ ↓ ↓
URL Match UI API/Data
│
↓
RouterProvider
│
↓
React App
For nested routes:
/dashboard
│
↓
Dashboard
│
└── <Outlet />
│
├── Profile
└── Settings
21. createBrowserRouter vs BrowserRouter
This is an important interview question.
Traditional declarative approach
<BrowserRouter>
<Routes>
<Route
path="/"
element={<Home />}
/>
</Routes>
</BrowserRouter>
Data Router approach
const router = createBrowserRouter([
{
path: "/",
Component: Home,
},
]);
Then:
<RouterProvider router={router} />
The second approach gives you access to the Data Router features such as:
loaders
actions
route-level errors
revalidation
lazy route modules
React Router's docs distinguish Declarative Mode from Data Mode, with createBrowserRouter being the browser runtime API for Data Mode. (React Router)
22. element vs Component
You will see both of these:
{
path: "/",
element: <Home />,
}
and:
{
path: "/",
Component: Home,
}
Both are supported in route configuration. The current Route Object API documents Component and element as alternatives. (React Router)
For a current createBrowserRouter setup, I would generally write:
{
path: "/",
Component: Home,
}
because it is clean and aligns with the current documentation examples.
23. One More Option: JSX Route Definitions
If you prefer the older JSX-style syntax but still want a Data Router, React Router provides:
createRoutesFromElements()
For example:
const routes = createRoutesFromElements(
<>
<Route
path="/"
Component={Home}
/>
<Route
path="/products"
Component={Products}
/>
</>
);
const router = createBrowserRouter(routes);
Then:
<RouterProvider router={router} />
React Router explicitly supports converting JSX <Route> elements into route objects with createRoutesFromElements(). (React Router)
But if you're learning the modern createBrowserRouter approach, I recommend learning the route-object format first.
Final Mental Model
For your blog, I would summarize the modern React Router architecture like this:
createBrowserRouter()
↓
Route Objects
↓
┌──────────────┼──────────────┐
↓ ↓ ↓
path Component loader
↓ ↓ ↓
URL Match UI Data
│
↓
RouterProvider
↓
React renders
And the most important APIs to learn are:
createBrowserRouter
↓
RouterProvider
↓
Route Objects
↓
Component
↓
children
↓
Outlet
↓
Link / NavLink
↓
useNavigate
↓
useParams
↓
useSearchParams
↓
loader / useLoaderData
↓
action / Form
One correction to the previous article: if you're targeting the latest React Router APIs, don't center the blog around <BrowserRouter> + <Routes> + <Route> as the primary architecture. That's still a valid declarative mode, but createBrowserRouter() + RouterProvider is the better modern starting point when you want React Router's Data APIs. (React Router)