DEV Community

Cover image for Learn React Router v6: Building a Feature-Rich Blog Application with Advanced Routing Techniques
Fred Zugs
Fred Zugs

Posted on • Updated on

Learn React Router v6: Building a Feature-Rich Blog Application with Advanced Routing Techniques

Table of Contents

Introduction

In this article, we'll explore advanced routing techniques while building a feature-rich blog application with React. Let's delve deeply into the concept of nested routes, dynamic routing, custom route components, manual navigation, protected routes, and lazy loading.

Prerequisites

  • HTML, CSS, and JavaScript: A strong foundation in HTML, CSS, and JavaScript is essential for comprehending the fundamentals of React Router. HTML provides the structure of web pages, CSS styles the appearance, and JavaScript adds interactivity.
  • React: Familiarity with React, a JavaScript library for building user interfaces, is crucial as React Router is tightly integrated with React components and rendering.
  • Node.js and npm/yarn: Node.js, a JavaScript runtime environment, and npm or yarn, package managers, are required to set up the development environment and manage JavaScript dependencies.
  • Terminal Basics: Basic proficiency in using the command line interface (CLI) or terminal is necessary for navigating the development environment, running commands, and interacting with the React Router package.

Setting Up the Project

To get started, let's set up our project. I always use vite, just a personal prefrence, you can do it your way or follow the steps in this article, to create a vite app run this command in your terminal:

npx create-vite your-project-name --template react
Enter fullscreen mode Exit fullscreen mode

Replace your-project-name with a project name of your choice while --template react indicates you want to create a vite project using the react template

Now install the latest version of React Router v6:

npm install react-router-dom@latest
Enter fullscreen mode Exit fullscreen mode

1. Creating Pages

In our blog application, we'll need different pages to showcase our content. Let's create separate components for the Home, Blog Post, and About pages, I encourage you to add your own ideas to the project.

// src/pages/Home.jsx
import React from 'react'

const Home = () => {
  return (
    <div>
      Home Page
    </div>
  )
}

export default Home

// src/pages/Blog.jsx
import React from 'react'

const Blog = () => {
  return (
    <div>
      Blog
    </div>
  )
}

export default Blog


// src/pages/About.jsx
import React from 'react'

const About = () => {
  return (
    <div>
      About page
    </div>
  )
}

export default About
Enter fullscreen mode Exit fullscreen mode

These pages will serve as the foundation for our blog application.

Nested Routes

Nested routes in React help us organize our web applications more neatly. Instead of having all the different parts of a webpage at the same level, nested routes let us group them together in a parent-child relationship.

Modular Components

Nested routes encourage modularity by allowing you to encapsulate related functionality within individual components. Each nested route can have its own set of components, enhancing code organization and readability.

We can effectively use Nested routes to structure our blog application. Here is how:

Hierarchy and Structure

Nested routes involve the creation of parent-child relationships between routes, mirroring the structure of the user interface.

Imagine a website like a house. The house has different rooms, each representing a different section of the website.Nested routes are like doorways between these rooms. The parent route is like the main entrance to the house, and the child routes are like the doorways to the individual rooms.

For example, in a blog website, the parent route could be "/blog," which would show a list of all the blog posts. The child routes could be "/blog/:id," where ":id" is the ID of a specific blog post. When a user clicks on a blog post, the child route would be activated, and the corresponding blog post would be shown.

To create a structured navigation experience, we'll implement nested routes. Open the App.js file and define routes for each page we created earlier, including their nested routes.

// src/App.js
// Import React Router components
import { BrowserRouter as Router, Link, Routes, Route } from 'react-router-dom';
// Import pages
import Home from './pages/Home';
import Blog from './pages/Blog';
import About from './pages/About';

// Define the App component
function App() {

// Render the Router component
  return (
    <Router>
      <nav>
        <Link to="/"> Home </Link>
        <Link to="/blog"> Blog </Link>
        <Link to="/about"> About </Link>
      </nav>

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/blog" element={<Blog />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </Router>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode
  • The Home route matches the root URL /, and renders the Home component.
  • The Blog route matches the URL /blog, and renders the Blog component.
  • The About route matches the URL /about, and renders the About component.

The text displayed should change when you click on the nav bar links, your routes should also change, For instance when you click on Blog; displayed text should be Blog and your route should be http://localhost:5173/blog

Now, our application is equipped with a basic routing structure.

Dynamic Routing

Dynamic routing in React Router allows you to create routes that can handle a variety of URLs based on specific parameters. This is particularly useful for applications with a large number of pages or components that need to handle different URLs based on specific data.

Here enhance the blog page to dynamically render content based on the URL parameter. This is where dynamic routing comes into play.

// src/pages/Blog.jsx
// The useParams hook is imported from the react-router-dom package, providing access to the dynamic parameters of the current URL
import { useParams, Link } from 'react-router-dom';

// Sample data for three blog posts
const blogPosts = [
  {
    id: '1',
    title: 'Blog Post 1',
    content: 'This is the content of Blog Post 1.',
  },
  {
    id: '2',
    title: 'Blog Post 2',
    content: 'This is the content of Blog Post 2.',
  },
  {
    id: '3',
    title: 'Blog Post 3',
    content: 'This is the content of Blog Post 3.',
  },
];

// Component for rendering a single blog post
const BlogPost = ({ post }) => (
  <div>
    <h2>{post.title}</h2>
    <p>{post.content}</p>
  </div>
);

const Blog = () => {
  // Use useParams to get the postId from the URL
  const { postId } = useParams();

  // Find the selected blog post based on the postId
  const selectedPost = blogPosts.find(post => post.id === postId);

  return (
    <div>
      {postId ? (
        // If postId is present, render the detailed view of the selected blog post
        <BlogPost post={selectedPost} />
      ) : (
        // Otherwise, render the list of blog posts
        <div>
          <h1>Blog Posts</h1>
          <ul>
            {blogPosts.map(post => (
              <li key={post.id}>
                {/* Use Link to navigate to the detailed view when the post is clicked */}
                <Link to={`/blog/${post.id}`}>{post.title}</Link>
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
};

export default Blog;

Enter fullscreen mode Exit fullscreen mode

The Blog page uses the useParams hook to check the URL for a specific blog post ID. If an ID is found, it displays the detailed content of that post using the BlogPost component. Otherwise, it shows a list of all available blog posts as links, each linked to their detailed view using the Link component. This allows the page to dynamically adapt its content based on the user's chosen post.

We have to Update our App.js file for this to work properly, here is how yours should look now:

// Import React Router components
import { BrowserRouter as Router, Link, Routes, Route } from 'react-router-dom';

// Import pages
import Home from './pages/Home';
import Blog from './pages/Blog';
import About from './pages/About';

// Define the App component
function App() {

  // Render the Router component
  return (
    <Router>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/blog">Blog</Link>
        <Link to="/about">About</Link>
      </nav>

      <Routes>
        <Route path="/blog/:postId" element={<Blog />} />
        <Route path="/blog" element={<Blog />} />
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </Router>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Our updated blog page now integrates dynamic routing, unlocking a richer user experience. Clicking a blog post title no longer simply highlights the title; it serves as a gateway to the full post's content. This seamless transition eliminates the need for manual navigation, enabling users to explore your blog effortlessly and efficiently.

Route Components

Route components are a powerful feature of React Router that allows you to customize the behavior of your routes. They provide a way to combine route-specific logic and rendering into reusable components, making your code more organized, reusable, and maintainable.

Here's a simpler explanation: Imagine your blog application as a house. Each section, like the Home page or a specific Post, is a separate room. Route components act like door signs, directing users to the right room based on the URL they enter.

Adding these "door signs" is simple. You create individual components for each room (e.g., Home, Post) and then define routes in your main app file (think of it as the house blueprint). These routes tell the application which room to display based on the URL in the address bar (like a map).

For example, the route /post/:postId would lead the user to the "Post" room, while / would direct them to the "Home" room. This makes navigation easy and keeps your code organized, just like having clear signs in your house makes it easy for guests to find their way around!

While this example focuses on public navigation, remember that Route components can also control access and work with authentication systems. Think of it as adding locks to certain rooms to restrict access to authorized personnel. Although won't be added to our project, private routes are a powerful tool for building secure and controlled sections within your blog application.

Here's how we can integrate private routes components into a project:

import React from 'react'
// src/components/PrivateRoute.js
// This line imports the Navigate and Route components from the react-router-dom package. These components are used to handle navigation and routing within the React application.

import { Navigate, Route } from 'react-router-dom';

const PrivateRoute = ({ element, isAuthenticated, ...rest }) => {
 return isAuthenticated ? (
  <Route {...rest} element={element} />
 ) : (
  <Navigate to="/login" replace />
 );
};


export default ProtectedRoute
Enter fullscreen mode Exit fullscreen mode

This PrivateRoute component checks if the user is authenticated. If authenticated, it renders the element component, which is the component that the route should normally render. If not authenticated, it redirects the user to the /login page.

const PrivateRoute = ({ element, isAuthenticated, ...rest }) => {
Enter fullscreen mode Exit fullscreen mode

This line defines a functional component named PrivateRoute. It takes three props:

  • element: The component that should be displayed if the user is logged in.
  • isAuthenticated: A boolean indicating whether the user is logged in.
  • ...rest: Any other props passed to the component.
return isAuthenticated ? (
 <Route {...rest} element={element} />
 ) : (
 <Navigate to="/login" replace />
 );
};
Enter fullscreen mode Exit fullscreen mode

This line implements the conditional rendering logic for the PrivateRoute. It checks if the isAuthenticated prop is true. If it is, it returns a Route component that renders the element component. Otherwise, it returns a Navigate component that redirects the user to the /login page.

Here's a more detailed explanation of the conditional logic:

isAuthenticated ? ( ... ) : ( ... ): This is a ternary operator that evaluates the isAuthenticated expression. If it's true, the first expression (...) is executed, and if it's false, the second expression (...) is executed.

<Route {...rest} element={element} />: This renders a Route component that passes all the props from ...rest to the component and also sets the element prop to the element prop.

<Navigate to="/login" replace />: This renders a Navigate component that redirects the user to the /login page and replaces the current URL in the browser's history. The replace prop ensures that the user cannot go back to the previous page using the browser's back button.

Using Route Components

To use a route component, you can wrap your route definition with the component:

<Route path="/protected" element={<PrivateRoute isAuthenticated={isAuthenticated} />} />
Enter fullscreen mode Exit fullscreen mode

This route will only render the Protected component if the user is authenticated. Otherwise, it will redirect the user to the login page.

Route components are a valuable tool for creating flexible and maintainable routing configurations in React Router applications. They allow you to combine route-specific logic and behavior into reusable components, making your code cleaner, more organized, and easier to manage.

Lazy Loading

Imagine your blog as a library. You wouldn't want to carry every book around just to explore a single chapter, right? Similarly, in web development, we can use "lazy loading" to load website content only when it's needed. This means faster loading times, smoother navigation, and happier readers! By focusing on delivering the essential information first, lazy loading makes your blog more efficient and user-friendly. Let's explore how to implement this powerful technique and enhance your blog's performance.

First let's install react-lazy:

npm install react-lazy
Enter fullscreen mode Exit fullscreen mode

Next in our App.jsx file, wrap the Blog component within the React.lazy function:

import React, { lazy } from 'react';
// ... other imports ...

const Blog = lazy(() => import('./pages/Blog'));
Enter fullscreen mode Exit fullscreen mode

Here's how your App.jsx file should look now:

import React, { lazy } from 'react';
import { BrowserRouter as Router, Link, Routes, Route } from 'react-router-dom';

// Import pages
import Home from './pages/Home';
import About from './pages/About';

// Lazy load the Blog component
const Blog = lazy(() => import('./pages/Blog'));

function App() {
 return (
  <Router>
   <nav>
    <Link to="/">Home</Link>
    {/* {isAuthenticated && <Link to="/blog">Blog</Link>} */}
    <Link to="/blog">Blog</Link>
     <Link to="/about">About</Link>
   </nav>


    <Routes>
     <Route path="/blog/:postId" element={<Blog />} />
     <Route path="/blog" element={<Blog />} />
     <Route path="/" element={<Home />} />
     <Route path="/about" element={<About />} />
     </Routes>
  </Router>
 );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

Conclusion

In conclusion, we've delved React Router v6, uncovering advanced routing techniques to enhance our web applications. From setting up the project with React Router v6 to creating dynamic and nested routes, we've laid the foundation for a feature-rich blog application. The introduction of route components brings a level of customization and organization, allowing us to handle authentication with finesse. Additionally, by embracing lazy loading, we've optimized our application for better performance.

As we wrap up, keep in mind that mastering these routing techniques opens the door to building more sophisticated and efficient React applications. The journey doesn't end here; there's always room for exploration and improvement as you continue to refine your skills in React development.

Top comments (0)