Route Navigation

In this chapter, you will learn how to use React Router v6 to implement page navigation without refreshing, including dynamic routes and parameter reading.


Why Use Routing?

Traditional multi-page websites request a new page from the server every time you click a link, causing white-screen flickering.

Frontend RoutingIt lets JavaScript control which component is displayed when the URL changes, without sending requests to the server.

Benefits:Smooth switching, experience feels like a native app。


Core Concepts of React Router v6

ConceptPurposeCorresponding Code
Route tableDefines the mapping between URLs and components<Routes> + <Route>
Route outletPlaceholder where child routes are displayed<Outlet />
Navigation linkClick to navigate<Link to="...">
Dynamic routeVariable in the URL/post/:id
Read parametersGet the value from the dynamic routeuseParams()

Installation and Configuration

$ npm install react-router-dom@6

Create Route Configuration

Example

// File path: src/router/index.jsx
import { createBrowserRouter } from 'react-router-dom'
import App from '../App'
import HomePage from '../pages/HomePage'
import PostPage from '../pages/PostPage'

// createBrowserRouter creates a router instance
const router = createBrowserRouter([
  {
    path: '/',                // App acts as the root layout
    element: <App />,
    children: [
      {
        index: true,          // index: true means the default child route for the / path
        element: <HomePage />
      },
      {
        path: 'post/:id',     // Dynamic route: :id matches any value
        element: <PostPage />
      }
    ]
  }
])

export default router

Two router modes:createBrowserRouterProduces clean URLs (/post/1), requiring server configuration. If the deployment environment is uncertain, usecreateHashRouter, the URL contains a # (/#/post/1), and no server configuration is needed.

Register the Router in main.jsx

Example

// File path: src/main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import { RouterProvider } from 'react-router-dom'
import router from './router'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    {/* RouterProvider injects the router into the app */}
    <RouterProvider router={router} />
  </React.StrictMode>
)

Use Outlet in App.jsx

Example

// File path: src/App.jsx
import { Outlet } from 'react-router-dom'
import NavBar from './components/NavBar'
import './App.css'

function App() {
  return (
    <div className="app">
      <NavBar />
      <main className="container">
        {/* Outlet is the outlet for nested routes: the component matched by child routes is rendered here */}
        <Outlet />
      </main>
      <footer className="footer">
        <p>© 2024 EXAMPLE Blog. Powered by React.</p>
      </footer>
    </div>
  )
}

export default App

Link — Declarative Navigation

LinkReplaces the traditional<a>tag and does not trigger a page refresh.

Example

import { Link } from 'react-router-dom'

function BlogCard({ id, title, summary, date, category }) {
  return (
    <Link to={`/post/${id}`} className="card-link">
      <div className="card">
        <span className="tag">{category}</span>
        <h3>{title}</h3>
        <p>{summary}</p>
        <span className="date">{date}</span>
      </div>
    </Link>
  )
}

Do not use<a href="....html">— it causes the browser to request a new page from the server, and the entire React app will reinitialize. Use<Link to="...">to switch on the client side without a refresh.


useParams — Reading Route Parameters

On the detail page, you need to extract the article ID from the URL to find the corresponding article.

Example

import { useParams } from 'react-router-dom'

function PostPage() {
  // useParams returns an object containing the parameters in the URL
  const { id } = useParams()
  // Note: id is a string; if you need a number, use Number(id) to convert it

  return (
    <div>
      <p>Current article ID:{id}</p>
    </div>
  )
}

useNavigate — Programmatic Navigation

In some scenarios, you need to navigate without clicking a link (e.g., after submitting a form, or navigating back to the home page when an article is not found).

Example

import { useNavigate } from 'react-router-dom'

function PostPage() {
  const navigate = useNavigate()

  const article = null  // Suppose the article is not found

  if (!article) {
    // When the article does not exist, navigate back to the home page
    return (
      <div>
        <p>Article not found</p>
        <button onClick={() => navigate('/')}>Back to home</button>
      </div>
    )
  }

  return <div>Article details...</div>
}

Vue3 vs React Router API Comparison

FeatureVue3React
Navigation link<RouterLink><Link>
Route outlet<RouterView><Outlet>
Dynamic route/post/:id/post/:id
Read parametersuseRoute().paramsuseParams()
Programmatic navigationuseRouter().push()useNavigate()

Hands-on: Complete Detail Page + Router Integration

Step 1: Modify BlogCard to Support Click Navigation

Example

// File path: src/components/BlogCard.jsx
import { Link } from 'react-router-dom'

function BlogCard({ id, title, summary, date, category }) {
  return (
    <Link to={`/post/${id}`} className="card-link">
      <div className="card">
        <span className="tag">{category}</span>
        <h3>{title}</h3>
        <p>{summary}</p>
        <span className="date">{date}</span>
      </div>
    </Link>
  )
}

export default BlogCard

Step 2: Create HomePage and PostPage

Example

// File path: src/pages/HomePage.jsx
import { useState, useMemo } from 'react'
import BlogCard from '../components/BlogCard'
import CategoryFilter from '../components/CategoryFilter'

function HomePage() {
  const [articles] = useState([
    { id: 1, title: 'The Complete Guide to React for Beginners', summary: 'Learn React from Scratch', category: 'React', date: '2024-05-10',
      content: '<h2>Why Learn React?</h2><p>React is currently one of the most popular frontend frameworks...</p>' },
    { id: 2, title: 'JS Async Programming Explained', summary: 'Understand Promise and async/await', category: 'JavaScript', date: '2024-05-08',
      content: '<h2>What is Async?</h2><p>JS is single-threaded; asynchronous operations can keep the main thread unblocked...</p>' },
    { id: 3, title: 'CSS Grid Layout Practice', summary: 'Use Grid to Create Responsive Layouts', category: 'CSS', date: '2024-05-05',
      content: '<h2>Getting Started with Grid</h2><p>Grid is a two-dimensional layout system...</p>' },
  ])

  const [activeCategory, setActiveCategory] = useState('All')

  const categories = useMemo(() => {
    return ['All', ...new Set(articles.map(a => a.category))]
  }, [articles])

  const filteredArticles = useMemo(() => {
    if (activeCategory === 'All') return articles
    return articles.filter(a => a.category === activeCategory)
  }, [articles, activeCategory])

  return (
    <div>
      <h2 className="section-title">Latest Articles</h2>
      <CategoryFilter
        categories={categories}
        activeCategory={activeCategory}
        onCategoryChange={setActiveCategory}
      />
      <p className="result-info">common{filteredArticles.length}article</p>
      {filteredArticles.length === 0 ? (
        <p className="empty-tip">No articles in this category yet</p>
      ) : (
        <div className="article-grid">
          {filteredArticles.map(article => (
            <BlogCard key={article.id} {...article} />
          ))}
        </div>
      )}
    </div>
  )
}

export default HomePage

Example

// File path: src/pages/PostPage.jsx
import { useParams, Link } from 'react-router-dom'
import { useMemo } from 'react'

// Article data (will be loaded from external sources in later chapters)
const articles = [
  { id: 1, title: 'The Complete Guide to React for Beginners', category: 'React', date: '2024-05-10',
    content: '<h2>Why Learn React?</h2><p>React is currently one of the most popular frontend frameworks...</p>' },
  { id: 2, title: 'JS Async Programming Explained', category: 'JavaScript', date: '2024-05-08',
    content: '<h2>What is Async?</h2><p>JS is single-threaded...</p>' },
]

function PostPage() {
  const { id } = useParams()

  // Find the article by ID
  const article = useMemo(() => {
    return articles.find(a => a.id === Number(id))
  }, [id])

  // Article not found
  if (!article) {
    return (
      <div className="not-found">
        <h2>Article not found</h2>
        <p>Article with ID{id}was not found.</p>
        <Link to="/">Back to home</Link>
      </div>
    )
  }

  // Article exists
  return (
    <article className="post-view">
      <span className="category-tag">{article.category}</span>
      <h1>{article.title}</h1>
      <time>{article.date}</time>
      {/* dangerouslySetInnerHTML is equivalent to Vue's v-html, be aware of XSS risks */}
      <div
        className="content"
        dangerouslySetInnerHTML={{ __html: article.content }}
      />
      <Link to="/" className="back-link">← Back to home</Link>
    </article>
  )
}

export default PostPage

dangerouslySetInnerHTMLis the React version ofv-html. The name is deliberately long to remind you: don't use it to render user-generated content, there is an XSS security risk. The content here is written by the developers themselves, so it is safe.

Add Styles for PostPage

Example

/* File path: append detail page styles to src/App.css */
.post-view {
  max-width: 720px;
  margin: 0 auto;
}

.category-tag {
  display: inline-block;
  padding: 4px 12px;
  background: #e3f2fd;
  color: #1976d2;
  border-radius: 12px;
  font-size: 13px;
  margin-bottom: 12px;
}

.post-view h1 {
  font-size: 32px;
  margin-bottom: 12px;
  line-height: 1.4;
}

.post-view time {
  display: block;
  color: #999;
  font-size: 14px;
  margin-bottom: 30px;
}

.content {
  line-height: 1.8;
  font-size: 16px;
  color: #333;
}

.content h2 {
  margin: 24px 0 12px;
  font-size: 22px;
}

.content p {
  margin-bottom: 12px;
}

.back-link {
  display: inline-block;
  margin-top: 40px;
  color: #1976d2;
  text-decoration: none;
}

.not-found {
  text-align: center;
  padding: 60px 0;
}

Chapter Summary

In this chapter, you learned the core of React Router v6: route table configuration (createBrowserRouter), Link navigation, Outlet nested outlet, dynamic route /post/:id, useParams to read parameters, and useNavigate for programmatic navigation.

Now the blog has a true multi-page experience — browse the list on the home page, click to enter the detail page to read the full content.

Other Extensions