Skip to content

React Router for Multi-Page Apps

Bulletin has multiple pages: a feed, post detail, user profiles, login, and register. React Router makes navigation work without full page reloads.

Terminal window
npm install react-router-dom
/ → PostFeed (public)
/posts/:id → PostDetail (public)
/users/:id → UserProfile (public)
/login → LoginPage (redirect if authed)
/register → RegisterPage (redirect if authed)
/create → CreatePost (requires auth)
src/App.tsx
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
import { AuthProvider } from './context/AuthContext'
import Layout from './components/Layout'
import PostFeed from './pages/PostFeed'
import PostDetail from './pages/PostDetail'
import UserProfile from './pages/UserProfile'
import LoginPage from './pages/LoginPage'
import RegisterPage from './pages/RegisterPage'
import CreatePost from './pages/CreatePost'
import ProtectedRoute from './components/ProtectedRoute'
function App() {
return (
<BrowserRouter>
<AuthProvider>
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<PostFeed />} />
<Route path="/posts/:id" element={<PostDetail />} />
<Route path="/users/:id" element={<UserProfile />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/register" element={<RegisterPage />} />
<Route path="/create" element={
<ProtectedRoute>
<CreatePost />
</ProtectedRoute>
} />
</Route>
</Routes>
</AuthProvider>
</BrowserRouter>
)
}

<Outlet /> renders the matched child route:

src/components/Layout.tsx
import { Outlet } from 'react-router-dom'
import Navbar from './Navbar'
export default function Layout() {
return (
<>
<Navbar />
<main className="container">
<Outlet />
</main>
</>
)
}
import { useNavigate } from 'react-router-dom'
function CreatePostForm() {
const navigate = useNavigate()
async function handleSubmit() {
const { data } = await postsApi.create({ title, body })
navigate(`/posts/${data.id}`) // Go to the new post
}
}
import { useParams } from 'react-router-dom'
function PostDetail() {
const { id } = useParams<{ id: string }>()
const postId = parseInt(id!)
const { data, loading, error } = useApi(() => postsApi.get(postId))
// ...
}
import { Link, NavLink } from 'react-router-dom'
// Regular link
<Link to={`/posts/${post.id}`}>{post.title}</Link>
// NavLink — adds 'active' class when the route matches
<NavLink to="/" end>Home</NavLink>
  1. Install react-router-dom.
  2. Set up the App.tsx with all routes.
  3. Create stub pages for each route (just an <h1> heading).
  4. Create the Layout component with Navbar and <Outlet />.
  5. Navigate between pages and verify routing works.
  • BrowserRouter + Routes + Route set up client-side routing.
  • Nested routes share layout — <Outlet /> renders the active child.
  • useNavigate() for programmatic navigation; Link/NavLink for declarative links.
  • useParams() reads dynamic URL segments like :id.
  • Wrap protected routes in a ProtectedRoute component (built in Module 09).