# React Router v7 Documentation

## Overview

React Router v7 is a multi-strategy router for React that bridges React 18 to React 19 incrementally. It's designed as a "user-obsessed, standards-focused, multi-strategy router you can deploy anywhere" that can function both as a minimal routing library or as a full React framework.

**Official Website**: https://reactrouter.com/  
**GitHub Repository**: https://github.com/remix-run/react-router  
**NPM Package**: react-router  
**Latest Version**: 7.7.1 (as of January 2025)

## Key Features

- **Non-breaking upgrade** from React Router v6
- **Framework capabilities** with optional adoption
- **First-class TypeScript support** with enhanced type safety
- **Multiple rendering strategies** (client-side, server-side, static pre-rendering)
- **Advanced data loading** and mutation capabilities
- **Streaming features** and lazy route loading
- **2.5+ billion npm downloads** with active community support

## Installation

### New Projects

Create a new React Router v7 project:

```bash
npx create-react-router@latest my-react-router-app
cd my-react-router-app
npm install
npm run dev
```

This starts a development server on `http://localhost:5173`.

### Existing Projects

For existing projects, install React Router v7:

```bash
# Remove old packages (if upgrading from v6)
npm uninstall react-router-dom

# Install v7 (simplified package structure)
npm install react-router@latest
```

## Usage Modes

React Router v7 supports three primary usage modes:

### 1. Declarative Mode
Traditional component-based routing using `<Router>`, `<Routes>`, and `<Route>` components.

### 2. Data Mode
Enhanced routing with data loading, actions, and server-side capabilities.

### 3. Framework Mode
Full framework features with file-based routing, bundling, and server rendering.

## Basic Routing

### Simple Route Configuration

```typescript
import { 
  createBrowserRouter, 
  RouterProvider,
  Route,
  createRoutesFromElements 
} from "react-router";

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path="/" element={<Root />}>
      <Route index element={<Home />} />
      <Route path="about" element={<About />} />
      <Route path="contact" element={<Contact />} />
      <Route path="users/:userId" element={<User />} />
    </Route>
  )
);

function App() {
  return <RouterProvider router={router} />;
}
```

### Advanced Route Configuration (Framework Mode)

```typescript
// routes.ts
import { route, index, layout, prefix } from "@react-router/dev/routes";
import type { RouteConfig } from "@react-router/dev/routes";

export default [
  index("./home.tsx"),
  route("about", "./about.tsx"),
  route("contact", "./contact.tsx"),
  
  // Nested routes with layout
  layout("./auth/layout.tsx", [
    route("login", "./auth/login.tsx"),
    route("register", "./auth/register.tsx"),
  ]),
  
  // Dynamic segments
  route("users/:userId", "./user.tsx"),
  route("posts/:postId", "./post.tsx"),
  
  // Optional segments
  route(":lang?/categories", "./categories.tsx"),
  
  // Splat/catchall routes
  route("files/*", "./files.tsx"),
] satisfies RouteConfig;
```

## Core Components

### Link Component

```typescript
import { Link } from "react-router";

function Navigation() {
  return (
    <nav>
      <Link to="/">Home</Link>
      <Link to="/about">About</Link>
      <Link to="/contact">Contact</Link>
    </nav>
  );
}
```

### NavLink Component (with active states)

```typescript
import { NavLink } from "react-router";

function Navigation() {
  return (
    <nav>
      <NavLink 
        to="/dashboard" 
        className={({ isActive }) => 
          isActive ? "nav-link active" : "nav-link"
        }
      >
        Dashboard
      </NavLink>
    </nav>
  );
}
```

### Outlet Component (for nested routes)

```typescript
import { Outlet } from "react-router";

function Layout() {
  return (
    <div>
      <header>My App Header</header>
      <main>
        <Outlet /> {/* Child routes render here */}
      </main>
      <footer>My App Footer</footer>
    </div>
  );
}
```

## Data Loading

### Loader Functions

```typescript
// user.tsx
import { useLoaderData } from "react-router";

// Loader function - runs before component renders
export async function loader({ params }) {
  const user = await fetchUser(params.userId);
  if (!user) {
    throw new Response("User not found", { status: 404 });
  }
  return user;
}

export default function User() {
  const user = useLoaderData<typeof loader>();
  
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}
```

### Client-Side Data Loading

```typescript
export async function clientLoader({ params, serverLoader }) {
  // Try to get from cache first
  const cached = getCachedUser(params.userId);
  if (cached) return cached;
  
  // Fall back to server loader
  return serverLoader();
}

export { loader as serverLoader };
```

## Actions and Forms

### Form Actions

```typescript
// contact.tsx
import { Form, redirect, useActionData } from "react-router";

export async function action({ request }) {
  const formData = await request.formData();
  const email = formData.get("email");
  const message = formData.get("message");
  
  try {
    await sendEmail({ email, message });
    return redirect("/thank-you");
  } catch (error) {
    return { error: "Failed to send message" };
  }
}

export default function Contact() {
  const actionData = useActionData<typeof action>();
  
  return (
    <Form method="post">
      <label>
        Email: <input name="email" type="email" required />
      </label>
      <label>
        Message: <textarea name="message" required />
      </label>
      {actionData?.error && (
        <p style={{ color: "red" }}>{actionData.error}</p>
      )}
      <button type="submit">Send</button>
    </Form>
  );
}
```

## Essential Hooks

### useNavigate

```typescript
import { useNavigate } from "react-router";

function LoginForm() {
  const navigate = useNavigate();
  
  const handleLogin = async (credentials) => {
    await login(credentials);
    navigate("/dashboard", { replace: true });
  };
  
  return (
    <form onSubmit={handleLogin}>
      {/* form fields */}
    </form>
  );
}
```

### useParams

```typescript
import { useParams } from "react-router";

function User() {
  const { userId } = useParams();
  
  return <div>User ID: {userId}</div>;
}
```

### useLocation

```typescript
import { useLocation } from "react-router";

function App() {
  const location = useLocation();
  
  return (
    <div>
      <p>Current path: {location.pathname}</p>
      <p>Search params: {location.search}</p>
    </div>
  );
}
```

### useSearchParams

```typescript
import { useSearchParams } from "react-router";

function SearchPage() {
  const [searchParams, setSearchParams] = useSearchParams();
  
  const query = searchParams.get("q") || "";
  
  const handleSearch = (newQuery) => {
    setSearchParams({ q: newQuery });
  };
  
  return (
    <div>
      <input 
        value={query}
        onChange={(e) => handleSearch(e.target.value)}
      />
      <p>Searching for: {query}</p>
    </div>
  );
}
```

### useNavigation (Pending UI)

```typescript
import { useNavigation } from "react-router";

function App() {
  const navigation = useNavigation();
  
  return (
    <div>
      {navigation.state === "loading" && <div>Loading...</div>}
      {navigation.state === "submitting" && <div>Saving...</div>}
      <Outlet />
    </div>
  );
}
```

### useFetcher (Non-navigational forms)

```typescript
import { useFetcher } from "react-router";

function NewsletterSignup() {
  const fetcher = useFetcher();
  
  const isSubscribing = fetcher.state === "submitting";
  
  return (
    <fetcher.Form method="post" action="/newsletter">
      <input name="email" type="email" placeholder="Your email" />
      <button disabled={isSubscribing}>
        {isSubscribing ? "Subscribing..." : "Subscribe"}
      </button>
    </fetcher.Form>
  );
}
```

## Error Handling

### Error Boundaries

```typescript
import { useRouteError, isRouteErrorResponse } from "react-router";

export function ErrorBoundary() {
  const error = useRouteError();
  
  if (isRouteErrorResponse(error)) {
    return (
      <div>
        <h1>{error.status} {error.statusText}</h1>
        <p>{error.data}</p>
      </div>
    );
  }
  
  return (
    <div>
      <h1>Something went wrong</h1>
      <p>{error.message || "Unknown error"}</p>
    </div>
  );
}
```

## Advanced Features

### Route Protection

```typescript
// protected-route.tsx
import { redirect } from "react-router";

export async function loader() {
  const user = await getCurrentUser();
  if (!user) {
    throw redirect("/login");
  }
  return user;
}

export default function ProtectedRoute() {
  const user = useLoaderData<typeof loader>();
  return <div>Welcome, {user.name}!</div>;
}
```

### Dynamic Route Segments

```typescript
// Dynamic segments: /users/:userId
// Optional segments: /:lang?/about
// Splat routes: /files/*

// Example: /blog/:year/:month/:slug
export async function loader({ params }) {
  const { year, month, slug } = params;
  const post = await getPost(year, month, slug);
  return post;
}
```

### Code Splitting with Lazy Loading

```typescript
import { lazy } from "react";

const LazyComponent = lazy(() => import("./heavy-component"));

// In your routes
route("heavy", lazy(() => import("./heavy-route")))
```

### Optimistic UI Updates

```typescript
import { useFetcher, useLoaderData } from "react-router";

function TodoItem({ todo }) {
  const fetcher = useFetcher();
  
  // Optimistic update
  const isCompleted = fetcher.formData 
    ? fetcher.formData.get("completed") === "true"
    : todo.completed;
    
  return (
    <div>
      <fetcher.Form method="post">
        <input 
          type="hidden" 
          name="completed" 
          value={!isCompleted} 
        />
        <button type="submit">
          {isCompleted ? "✓" : "○"} {todo.text}
        </button>
      </fetcher.Form>
    </div>
  );
}
```

## Server-Side Rendering (SSR)

### Basic SSR Setup

```typescript
// server.js
import { createRequestHandler } from "@react-router/express";
import express from "express";

const app = express();

app.all(
  "*",
  createRequestHandler({
    // Your build configuration
    build: await import("./build/server/index.js")
  })
);

app.listen(3000);
```

## Testing

### Testing with React Router

```typescript
import { createRoutesStub } from "react-router";
import { render, screen } from "@testing-library/react";

test("renders user profile", async () => {
  const Stub = createRoutesStub([
    {
      path: "/users/:userId",
      Component: UserProfile,
      loader: () => ({ name: "John Doe", email: "john@example.com" })
    }
  ]);
  
  render(<Stub initialEntries={["/users/123"]} />);
  
  expect(await screen.findByText("John Doe")).toBeInTheDocument();
});
```

## Migration from v6

### Key Changes in v7

1. **Simplified packages**: Only need `react-router` instead of `react-router-dom`
2. **No breaking changes**: If you've enabled all future flags in v6
3. **Enhanced TypeScript support**: Better type inference and safety
4. **Framework features**: Optional adoption of full-stack capabilities

### Migration Steps

```bash
# 1. Update dependencies
npm uninstall react-router-dom
npm install react-router@latest

# 2. Update imports
# Before (v6):
import { BrowserRouter } from "react-router-dom";

# After (v7):
import { BrowserRouter } from "react-router";
```

## Best Practices

1. **Use loaders for data fetching** - Load data before rendering
2. **Implement error boundaries** - Handle errors gracefully
3. **Leverage TypeScript** - Get full type safety
4. **Use nested routes** - Create reusable layouts
5. **Implement pending UI** - Show loading states
6. **Optimize with code splitting** - Load routes lazily
7. **Test your routes** - Use testing utilities
8. **Handle form submissions properly** - Use actions and fetchers

## Performance Tips

1. **Lazy load routes**: Use dynamic imports for code splitting
2. **Prefetch critical routes**: Load important routes in advance
3. **Use proper caching**: Implement client-side caching strategies
4. **Optimize data loading**: Use clientLoader for cached data
5. **Implement proper error boundaries**: Prevent crashes from propagating

## Resources

- [Official Documentation](https://reactrouter.com/)
- [GitHub Repository](https://github.com/remix-run/react-router)
- [NPM Package](https://www.npmjs.com/package/react-router)
- [Upgrade Guide from v6](https://reactrouter.com/upgrading/v6)
- [Community Discord](https://rmx.as/discord)
- [Changelog](https://reactrouter.com/start/changelog)

## Community Stats (2025)

- **2.5+ billion npm downloads**
- **55,266 GitHub stars**
- **1,212 contributors**
- **3,597,612 GitHub dependents**

React Router v7 represents the evolution of React routing from a simple library to a comprehensive framework while maintaining backward compatibility and developer-friendly APIs.