React Router Basics

Adding page navigation to a React app with react-router -- BrowserRouter, Routes, Route, Link, NavLink, and a Not Found page for undefined URLs, with simple examples.

Intermediate 12 min
TR

React Router Basics

Every example we've seen so far has been a SINGLE page. Real applications usually have more than one "page" -- a home page, a course list, an about page. This lesson covers how to do that in React, with the React Router library.

Why Do We Need a Router?

In a classic website, every page (/, /courses, /about) is a separate HTML file -- clicking a link makes the browser load a NEW page. React applications, on the other hand, run as a single HTML file (a Single Page Application, SPA); "changing pages" actually means rendering DIFFERENT components on that same page, based on the URL. We use the react-router library to manage this -- it reads the URL and decides which component to show.

Defining Pages with BrowserRouter and Routes

The first step to using a router in a React app is wrapping the application in BrowserRouter, then adding Routes and Routes inside it:

import { BrowserRouter, Routes, Route } from "react-router";

function Home() {
  return <h1>Home Page</h1>;
}

function Courses() {
  return <h1>Courses Page</h1>;
}

function BasicRouterSetupExample() {
  return (
    <BrowserRouter>
      {/* Routes, URL'e göre HANGİ Route'un render edileceğine karar verir --
          aynı anda yalnızca eşleşen bir tane render edilir. */}
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/courses" element={<Courses />} />
      </Routes>
    </BrowserRouter>
  );
}

BrowserRouter is the component that watches the browser's URL and informs React of changes. Routes looks at the URL to figure out WHICH Route matches -- each Route has a path (a URL pattern) and an element (the component to show for that URL). Only the matching Route is rendered at any given time.

We DON'T use <a href="..."> to move between pages -- that makes the browser reload the entire page. Instead, we use react-router's Link component:

import { BrowserRouter, Routes, Route, Link } from "react-router";

function Home() {
  return (
    <div>
      <h1>Home Page</h1>
      {/* <a href="..."> yerine <Link to="..."> kullanıyoruz -- Link, sayfayı
          YENİDEN YÜKLEMEDEN (full page reload olmadan) URL'i değiştirir,
          React sadece gerekli kısmı yeniden render eder. */}
      <Link to="/courses">View Courses</Link>
    </div>
  );
}

function Courses() {
  return (
    <div>
      <h1>Courses Page</h1>
      <Link to="/">Back to Home</Link>
    </div>
  );
}

function LinkNavigationExample() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/courses" element={<Courses />} />
      </Routes>
    </BrowserRouter>
  );
}

<Link to="/courses"> looks and behaves like an <a> tag on screen, but clicking it does NOT reload the page -- it only changes the URL, and React renders the matching Route in response. This makes navigation much faster and smoother.

In a navigation menu, we usually want to show WHICH page the user is currently on -- for that, we use NavLink instead of Link:

import { BrowserRouter, Routes, Route, NavLink } from "react-router";

function Home() {
  return <h1>Home Page</h1>;
}

function Courses() {
  return <h1>Courses Page</h1>;
}

function NavLinkActiveExample() {
  return (
    <BrowserRouter>
      <nav>
        {/* NavLink, Link ile aynı işi yapar -- ama className'e bir fonksiyon
            vererek "şu an bu link'in sayfasındayım" durumunu (isActive)
            ayırt edebiliyoruz. */}
        <NavLink
          to="/"
          className={({ isActive }) => (isActive ? "nav-link active" : "nav-link")}
        >
          Home
        </NavLink>
        <NavLink
          to="/courses"
          className={({ isActive }) => (isActive ? "nav-link active" : "nav-link")}
        >
          Courses
        </NavLink>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/courses" element={<Courses />} />
      </Routes>
    </BrowserRouter>
  );
}

NavLink works exactly like Link, but lets you pass a FUNCTION to className (or style); that function receives an { isActive } object telling you whether that link's page is the current one.

Combining Multiple Pages

A real application usually has several pages together with a shared navigation menu:

import { BrowserRouter, Routes, Route, Link } from "react-router";

function Home() {
  return <h1>Home</h1>;
}

function Courses() {
  return <h1>Courses</h1>;
}

function About() {
  return <h1>About</h1>;
}

function MultiPageNavExample() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/courses">Courses</Link>
        <Link to="/about">About</Link>
      </nav>

      {/* Üç ayrı sayfa, üç ayrı Route -- her biri kendi component'ini
          render ediyor, URL değiştikçe React aralarında geçiş yapıyor. */}
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/courses" element={<Courses />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  );
}

Here there are three separate Routes (/, /courses, /about) and three Links pointing to them -- this is the basic skeleton of a small multi-page application.

Unmatched URLs: A Not Found Page

What happens if a user goes to a URL that isn't defined (like /does-not-exist)? A special Route catches this:

import { BrowserRouter, Routes, Route, Link } from "react-router";

function Home() {
  return <h1>Home</h1>;
}

function Courses() {
  return <h1>Courses</h1>;
}

function NotFound() {
  return (
    <div>
      <h1>404 - Page Not Found</h1>
      <Link to="/">Go back home</Link>
    </div>
  );
}

function NotFoundRouteExample() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/courses" element={<Courses />} />
        {/* path="*", tanımlı diğer hiçbir Route ile eşleşmeyen URL'leri
            YAKALAR -- her zaman Routes içindeki EN SONA yazılır. */}
        <Route path="*" element={<NotFound />} />
      </Routes>
    </BrowserRouter>
  );
}

path="*" catches every URL that doesn't match any OTHER Route -- that's why it's always written as the LAST Route inside Routes; React looks for a match from top to bottom, in order.

Summary and Glossary

BrowserRouter wraps the application and watches the URL; Routes and Route decide which component to show based on that URL. To move between pages, we use Link instead of <a> (or NavLink if we need to highlight the active page) -- both change the URL without reloading the page. Undefined URLs are caught by a Route written with path="*" and placed at the end of Routes.

Glossary

SPA (Single Page Application) — An application that runs on a single HTML file, managing "page changes" with JavaScript instead of reloading the browser.

Route — A definition that maps a URL pattern (path) to a specific component (element).

Client-Side Routing — Managing URL changes in the browser with JavaScript, without sending a new request to the server.