React Router is the routing layer of most React single-page apps, so interviews use it to check whether you understand client-side navigation, not just the API. The questions below cover how routing works without a page reload, defining nested and dynamic routes, reading params, navigating in code, guarding routes behind auth, and splitting bundles per route. Ground your answers in the History API and the single-page-app model and the details fall into place.
How to answer React Router questions
Start from what a single-page app needs: one HTML document, and JavaScript that swaps which components render as the URL changes — no server round-trip, no full reload. React Router does this by listening to the browser History API and matching the current URL against your route config. Every feature — nested routes, params, guards, lazy loading — is a layer on that core. Say the core first, then the specific.
Q1. Why does a single-page app need a router?
A single-page app loads one HTML page and then updates the view with JavaScript. Without a router, changing the URL would trigger a full server request and reload, losing app state. React Router intercepts navigation, updates the URL via the History API, and renders the matching component in place — keeping it a smooth, stateful single page while still giving each view a real, shareable URL.
The "real URL" part matters: users expect the back button, bookmarks and deep links to work. The router makes client-side view changes behave like normal web navigation.
Interview note: Follow-up: "what breaks if you use plain
<a href>for internal links?" It triggers a full page reload, discarding React state and re-downloading the app. UseLink/NavLink, which navigate client-side.
Q2. How do you define routes?
In current React Router you declare routes with a data router — commonly createBrowserRouter with an array of route objects, rendered by RouterProvider — or with the JSX <Routes>/<Route> form. Each route maps a URL path to an element, and routes can nest to share layout.
const router = createBrowserRouter([
{ path: "/", element: <Layout />, children: [
{ index: true, element: <Home /> },
{ path: "users/:id", element: <UserProfile /> },
]},
]);
// <RouterProvider router={router} />
Interview note: Follow-up: "data router vs
<Routes>?" The data router (createBrowserRouter) unlocks loaders, actions and better data APIs; the JSX<Routes>form is simpler and fine for basic routing. Know both exist.
Q3. What are nested routes and the Outlet?
Nested routes let a parent route render shared layout while child routes render into it. The parent renders an <Outlet /> where the matched child appears, so a layout, sidebar or tab shell is written once and every child route slots into it.
function Layout() {
return (
<div>
<NavBar />
<main><Outlet /></main> {/* matched child route renders here */}
</div>
);
}
Interview note: Trap: "child route renders but the layout disappears — why?" You forgot the
<Outlet />in the parent, so there is nowhere for the child to render. The parent must include it.
Q4. How do dynamic routes and useParams work?
A dynamic segment like path: "users/:id" matches any value in that position, and useParams() returns those values as an object — const { id } = useParams(). This is how detail pages read which record to show from the URL.
function UserProfile() {
const { id } = useParams();
// fetch or look up the user by id
return <h2>User {id}</h2>;
}
Interview note: Follow-up: "how do you read
?tab=settings?" That is a query string, not a path param — useuseSearchParams(), which returns aURLSearchParamsand a setter, notuseParams.
Q5. How do you navigate programmatically vs declaratively?
Declarative navigation uses <Link to="..."> or <NavLink> (which adds active styling) in JSX. Programmatic navigation uses the useNavigate hook — const navigate = useNavigate() — and you call navigate("/dashboard") after an event like a successful login or form submit.
const navigate = useNavigate();
async function onLogin(credentials) {
await login(credentials);
navigate("/dashboard", { replace: true }); // replace: no back-to-login
}
Interview note: Trap: "why
replace: trueafter login?" So the back button does not return the user to the login page they just left;replaceswaps the history entry instead of pushing a new one.
Q6. How do you implement a protected route?
Create a guard component that reads auth state and either renders the nested route via <Outlet /> or redirects to login with <Navigate to="/login" replace />. Nest the protected routes under this guard so the check runs before they render.
function RequireAuth() {
const { user } = useAuth();
return user ? <Outlet /> : <Navigate to="/login" replace />;
}
// In routes: { element: <RequireAuth />, children: [ { path: "dashboard", ... } ] }
Interview note: Follow-up: "how do you send the user back after login?" Capture the attempted location (
useLocation) and pass it to the login page, thennavigatethere after auth — a redirect-back pattern.
Q7. How do loaders change data fetching in React Router?
A data router lets you attach a loader to a route that fetches data before the route renders, so the component gets data via useLoaderData() without a loading-in-effect dance. This moves fetching out of useEffect, avoids waterfalls, and lets the router show pending UI during navigation.
The interview point is the shift: instead of "render, then fetch in an effect, then show a spinner," loaders fetch during navigation, so the component renders with data ready.
Interview note: Trap: "does a loader replace React Query?" Not necessarily — loaders handle route-level fetching and pending states; a caching library still helps with client-side caching, background refetch and mutations. They compose.
Q8. How do you code-split routes for performance?
Load route components with React.lazy(() => import("./Page")) and wrap the routed area in <Suspense fallback={...}>, so each route's JavaScript downloads only when the user navigates to it. This shrinks the initial bundle — often the biggest lever for first-load performance.
const Reports = React.lazy(() => import("./Reports"));
// <Suspense fallback={<Spinner />}><Reports /></Suspense>
Route-based splitting is the most natural split point because users rarely need every page's code upfront. The performance optimization questions cover the wider tactics.
Interview note: Follow-up: "what happens without the Suspense boundary?" A lazy component with no Suspense above it throws; the boundary provides the fallback while the chunk loads.
Q9. How does client-side routing handle a page refresh or deep link on a server?
Because all routes are served by one HTML entry point, the server must be configured to return that entry (a catch-all/rewrite) for any path, so refreshing /users/42 does not 404. The client router then reads the URL and renders the right view. This server rewrite is a common gotcha when deploying an SPA.
Interview note: Trap: "everything works in dev but refreshing a deep route 404s in production — why?" The dev server rewrites unknown paths to
index.html; production must be configured to do the same, or use a framework that handles routing on the server.
How to prepare
Build a small app with a layout route, an index child, a dynamic :id detail route, a protected section and one lazily-loaded page — that single project touches almost every question here. Practise explaining why client routing avoids a reload and why internal links must be Link not <a>, since those reveal whether you understand the model or just memorised hooks. Pair this with the components and props foundation and the performance optimization set for the code-splitting angle, refresh routing patterns in the React learning path, and use a mock interview to rehearse the deep-link and protected-route follow-ups.
Frequently Asked Questions
How does client-side routing differ from server-side routing?
How do you read URL parameters in React Router?
How do you navigate programmatically?
How do you protect a route behind authentication?
How do you code-split by route?
Want to Build Your Career in Java Full Stack with AI?
Join CodeBegun and train with working industry engineers — View the Java Full Stack curriculum

