Hello! In our last lesson, you set up the basic structure for a single-page application (SPA) using React Router. You learned how to define routes that map URL paths to specific components and how to handle 404 "Not Found" errors. However, to navigate between pages, you had to manually type the path into the browser's address bar.
This lesson builds directly on that foundation. We will address the learning outcome: Define routes and create navigation links between different pages. You will learn how to use React Router's special components to create clickable links that enable smooth, client-side navigation, transforming your static views into a truly interactive SPA.
Why Not Use a Regular <a> Tag?
In HTML, you create hyperlinks using the <a> tag with an href attribute. If you use a standard <a> tag in a React SPA, it will trigger a full-page refresh every time a user clicks a link. This defeats the entire purpose of an SPA, which is to provide a fast, seamless experience by updating the page content with JavaScript instead of fetching a new HTML document from the server.
To solve this, React Router provides its own navigation component: <Link>.
Creating Navigation with the <Link> Component
The <Link> component is your primary tool for creating navigation. It renders an <a> tag under the hood but intercepts the click event, tells React Router to update the URL in the browser's history, and renders the new component without a page refresh.
Let's see how to implement this.
Learn React Router v6 In 45 Minutes
This clip from the 'Learn React Router v6' tutorial by Web Dev Simplified clearly explains and demonstrates how to replace standard anchor tags with the Link component.
Watch from 04:08 to 06:16. Pay attention to how the Link component is used with the to prop and the seamless navigation it provides compared to a full-page refresh.
As the video shows, the usage is straightforward:
- Import
Linkfromreact-router-dom. - Use
<Link to="/your-path">instead of<a href="/your-path">.
Creating a Shared Layout with <Outlet />
In most applications, you'll have elements like a navigation bar or a footer that should appear on every page. It would be inefficient to add the navbar component to every single page component (HomePage.jsx, AboutPage.jsx, etc.).
Instead, we can create a shared layout component. In the last lesson, we briefly introduced this idea by setting element: <App /> on our root route. Now, let's make it fully functional using the <Outlet /> component.
An <Outlet /> is a placeholder. Within a parent route's element (our layout), the <Outlet /> component tells React Router where to render the content of the matched child route.
Let's put this into practice:
-
Create a
Navbarcomponent: Create a new filesrc/components/Navbar.jsx.// src/components/Navbar.jsx import { Link } from 'react-router-dom'; function Navbar() { return ( <nav style={{ display: 'flex', gap: '1rem', paddingBottom: '1rem', borderBottom: '1px solid #ccc' }}> <Link to="/">Home</Link> <Link to="/about">About</Link> </nav> ); } export default Navbar; -
Update
App.jsxto be the layout: Modify yourApp.jsxto include theNavbarand theOutlet.// src/App.jsx import { Outlet } from 'react-router-dom'; import Navbar from './components/Navbar'; function App() { return ( <div> <Navbar /> <main style={{ paddingTop: '1rem' }}> <Outlet /> {/* Child routes will be rendered here */} </main> </div> ); } export default App;
Your main.jsx router configuration from the previous lesson should already be set up to use App as the layout for its children routes, but for clarity, it looks like this:
// src/main.jsx
// ...imports
const router = createBrowserRouter([
{
path: "/",
element: <App />, // App is the layout component
errorElement: <NotFoundPage />,
children: [
{
index: true,
element: <HomePage />,
},
{
path: "about",
element: <AboutPage />,
},
],
},
]);
// ...
Now, when you run your app, the Navbar will be visible on both the Home and About pages, and clicking the links will swap the content rendered in the <Outlet />.
This pattern is fundamental to building scalable React applications. To see a detailed walkthrough of creating nested layouts with an <Outlet />, you can refer to this resource.
Learn React Router v6 In 45 Minutes
This section of the Web Dev Simplified video provides a comprehensive example of creating a shared layout for a group of nested routes using the <Outlet /> component.
Watch the segment from 16:42 to 21:20. The video demonstrates creating a BookLayout component that acts as a parent for several book-related routes, using <Outlet /> to render the specific child pages. This reinforces the concept we just applied with our App layout.
Test your understanding!
Let's add a "Contact" page to our application. Following the pattern we just established, what are the three steps you need to take?
Show answer
- Create the page component: Create a new file
src/pages/ContactPage.jsxwith some basic content, like<h1>Contact Us</h1>. - Update the router: In
main.jsx, importContactPageand add a new route object to thechildrenarray:{ path: "contact", element: <ContactPage /> }. - Update the navigation: In
src/components/Navbar.jsx, add a new<Link>component:<Link to="/contact">Contact</Link>.
Highlighting the Active Link with <NavLink />
Our navigation works, but it doesn't provide any visual feedback to the user about which page they are currently on. For a better user experience, the link corresponding to the active page should be styled differently (e.g., bolded or colored).
React Router provides a special version of <Link> called <NavLink> for this exact purpose. It automatically adds an active class to the rendered <a> tag when its to prop matches the current URL.
You can then style this class in your CSS file:
/* In index.css or another global stylesheet */
.active {
color: #007bff;
font-weight: bold;
text-decoration: none;
}
Simply replace <Link> with <NavLink> in your Navbar.jsx component.
// src/components/Navbar.jsx
import { NavLink } from 'react-router-dom'; // Changed import
function Navbar() {
return (
<nav style={{ display: 'flex', gap: '1rem', paddingBottom: '1rem', borderBottom: '1px solid #ccc' }}>
{/* Use NavLink instead of Link */}
<NavLink to="/">Home</NavLink>
<NavLink to="/about">About</NavLink>
<NavLink to="/contact">Contact</NavLink>
</nav>
);
}
export default Navbar;
For more advanced styling, NavLink also allows you to pass a function to its style or className prop. This function receives an object with an isActive boolean property, giving you full control over the styles in your JSX.
<NavLink
to="/"
style={({ isActive }) => {
return {
fontWeight: isActive ? "bold" : "",
color: isActive ? "red" : "black",
};
}}
>
Home
</NavLink>
The video below covers these NavLink features in detail.
Learn React Router v6 In 45 Minutes
To see NavLink in action, watch this segment from the same Web Dev Simplified tutorial. It covers both the default active class and the more flexible function-based styling.
Watch from 32:48 to 36:30. This part effectively demonstrates how NavLink can be used to dynamically style active navigation links.
Conclusion
In this lesson, you have learned the essential skills to build a navigable multi-page experience within a single-page application. You can now create user-facing navigation that is both fast and intuitive.
Key Takeaways:
- Use the
<Link>component instead of an<a>tag to enable client-side routing and prevent full-page reloads. - The
<Outlet />component is a crucial tool for creating shared layouts (like navbars and footers) that persist across different pages. - Use the
<NavLink>component to automatically apply active styles to your navigation links, improving user experience by visually indicating the current page.
So far, all our routes point to static pages. However, many real-world applications need to display pages based on dynamic data, such as a user's profile or a specific product detail page. In the next lesson, you will learn how to use route parameters to create dynamic pages that display specific content, allowing you to build routes like /users/123 or /products/456.