| 1 | ---
|
| 2 | title: Routing
|
| 3 | order: 2
|
| 4 | ---
|
| 5 |
|
| 6 | # Routing
|
| 7 |
|
| 8 | [MODES: declarative]
|
| 9 |
|
| 10 | ## Configuring Routes
|
| 11 |
|
| 12 | Routes are configured by rendering `<Routes>` and `<Route>` that couple URL segments to UI elements.
|
| 13 |
|
| 14 | ```tsx
|
| 15 | import React from "react";
|
| 16 | import ReactDOM from "react-dom/client";
|
| 17 | import { BrowserRouter, Routes, Route } from "react-router";
|
| 18 | import App from "./app";
|
| 19 |
|
| 20 | const root = document.getElementById("root");
|
| 21 |
|
| 22 | ReactDOM.createRoot(root).render(
|
| 23 | <BrowserRouter>
|
| 24 | <Routes>
|
| 25 | <Route path="/" element={<App />} />
|
| 26 | </Routes>
|
| 27 | </BrowserRouter>,
|
| 28 | );
|
| 29 | ```
|
| 30 |
|
| 31 | Here's a larger sample config:
|
| 32 |
|
| 33 | ```tsx
|
| 34 | <Routes>
|
| 35 | <Route index element={<Home />} />
|
| 36 | <Route path="about" element={<About />} />
|
| 37 |
|
| 38 | <Route element={<AuthLayout />}>
|
| 39 | <Route path="login" element={<Login />} />
|
| 40 | <Route path="register" element={<Register />} />
|
| 41 | </Route>
|
| 42 |
|
| 43 | <Route path="concerts">
|
| 44 | <Route index element={<ConcertsHome />} />
|
| 45 | <Route path=":city" element={<City />} />
|
| 46 | <Route path="trending" element={<Trending />} />
|
| 47 | </Route>
|
| 48 | </Routes>
|
| 49 | ```
|
| 50 |
|
| 51 | ## Nested Routes
|
| 52 |
|
| 53 | Routes can be nested inside parent routes.
|
| 54 |
|
| 55 | ```tsx
|
| 56 | <Routes>
|
| 57 | <Route path="dashboard" element={<Dashboard />}>
|
| 58 | <Route index element={<Home />} />
|
| 59 | <Route path="settings" element={<Settings />} />
|
| 60 | </Route>
|
| 61 | </Routes>
|
| 62 | ```
|
| 63 |
|
| 64 | The path of the parent is automatically included in the child, so this config creates both `"/dashboard"` and `"/dashboard/settings"` URLs.
|
| 65 |
|
| 66 | Child routes are rendered through the `<Outlet/>` in the parent route.
|
| 67 |
|
| 68 | ```tsx filename=app/dashboard.tsx
|
| 69 | import { Outlet } from "react-router";
|
| 70 |
|
| 71 | export default function Dashboard() {
|
| 72 | return (
|
| 73 | <div>
|
| 74 | <h1>Dashboard</h1>
|
| 75 | {/* will either be <Home/> or <Settings/> */}
|
| 76 | <Outlet />
|
| 77 | </div>
|
| 78 | );
|
| 79 | }
|
| 80 | ```
|
| 81 |
|
| 82 | ## Layout Routes
|
| 83 |
|
| 84 | Routes _without_ a `path` create new nesting for their children, but they don't add any segments to the URL.
|
| 85 |
|
| 86 | ```tsx lines=[2,9]
|
| 87 | <Routes>
|
| 88 | <Route element={<MarketingLayout />}>
|
| 89 | <Route index element={<MarketingHome />} />
|
| 90 | <Route path="contact" element={<Contact />} />
|
| 91 | </Route>
|
| 92 |
|
| 93 | <Route path="projects">
|
| 94 | <Route index element={<ProjectsHome />} />
|
| 95 | <Route element={<ProjectsLayout />}>
|
| 96 | <Route path=":pid" element={<Project />} />
|
| 97 | <Route path=":pid/edit" element={<EditProject />} />
|
| 98 | </Route>
|
| 99 | </Route>
|
| 100 | </Routes>
|
| 101 | ```
|
| 102 |
|
| 103 | ## Index Routes
|
| 104 |
|
| 105 | Index routes render into their parent's `<Outlet/>` at their parent's URL (like a default child route). They are configured with the `index` prop:
|
| 106 |
|
| 107 | ```tsx lines=[4,8]
|
| 108 | <Routes>
|
| 109 | <Route path="/" element={<Root />}>
|
| 110 | {/* renders into the outlet in <Root> at "/" */}
|
| 111 | <Route index element={<Home />} />
|
| 112 |
|
| 113 | <Route path="dashboard" element={<Dashboard />}>
|
| 114 | {/* renders into the outlet in <Dashboard> at "/dashboard" */}
|
| 115 | <Route index element={<DashboardHome />} />
|
| 116 | <Route path="settings" element={<Settings />} />
|
| 117 | </Route>
|
| 118 | </Route>
|
| 119 | </Routes>
|
| 120 | ```
|
| 121 |
|
| 122 | Note that index routes can't have children. If you're expecting that behavior, you probably want a [layout route](#layout-routes).
|
| 123 |
|
| 124 | ## Route Prefixes
|
| 125 |
|
| 126 | A `<Route path>` _without_ an `element` prop adds a path prefix to its child routes, without introducing a parent layout.
|
| 127 |
|
| 128 | ```tsx filename=app/routes.ts lines=[1]
|
| 129 | <Route path="projects">
|
| 130 | <Route index element={<ProjectsHome />} />
|
| 131 | <Route element={<ProjectsLayout />}>
|
| 132 | <Route path=":pid" element={<Project />} />
|
| 133 | <Route path=":pid/edit" element={<EditProject />} />
|
| 134 | </Route>
|
| 135 | </Route>
|
| 136 | ```
|
| 137 |
|
| 138 | ## Dynamic Segments
|
| 139 |
|
| 140 | If a path segment starts with `:` then it becomes a "dynamic segment". When the route matches the URL, the dynamic segment will be parsed from the URL and provided as `params` to other router APIs like `useParams`.
|
| 141 |
|
| 142 | ```tsx
|
| 143 | <Route path="teams/:teamId" element={<Team />} />
|
| 144 | ```
|
| 145 |
|
| 146 | ```tsx filename=app/team.tsx
|
| 147 | import { useParams } from "react-router";
|
| 148 |
|
| 149 | export default function Team() {
|
| 150 | let params = useParams();
|
| 151 | // params.teamId
|
| 152 | }
|
| 153 | ```
|
| 154 |
|
| 155 | You can have multiple dynamic segments in one route path:
|
| 156 |
|
| 157 | ```tsx
|
| 158 | <Route
|
| 159 | path="/c/:categoryId/p/:productId"
|
| 160 | element={<Product />}
|
| 161 | />
|
| 162 | ```
|
| 163 |
|
| 164 | ```tsx filename=app/category-product.tsx
|
| 165 | import { useParams } from "react-router";
|
| 166 |
|
| 167 | export default function CategoryProduct() {
|
| 168 | let { categoryId, productId } = useParams();
|
| 169 | // ...
|
| 170 | }
|
| 171 | ```
|
| 172 |
|
| 173 | You should ensure that all dynamic segments in a given path are unique. Otherwise, as the `params` object is populated - latter dynamic segment values will override earlier values.
|
| 174 |
|
| 175 | ## Optional Segments
|
| 176 |
|
| 177 | You can make a route segment optional by adding a `?` to the end of the segment.
|
| 178 |
|
| 179 | ```tsx
|
| 180 | <Route path=":lang?/categories" element={<Categories />} />
|
| 181 | ```
|
| 182 |
|
| 183 | You can have optional static segments, too:
|
| 184 |
|
| 185 | ```tsx
|
| 186 | <Route path="users/:userId/edit?" element={<User />} />
|
| 187 | ```
|
| 188 |
|
| 189 | ## Splats
|
| 190 |
|
| 191 | Also known as "catchall" and "star" segments. If a route path pattern ends with `/*` then it will match any characters following the `/`, including other `/` characters.
|
| 192 |
|
| 193 | ```tsx
|
| 194 | <Route path="files/*" element={<File />} />
|
| 195 | ```
|
| 196 |
|
| 197 | ```tsx
|
| 198 | let params = useParams();
|
| 199 | // params["*"] will contain the remaining URL after files/
|
| 200 | let filePath = params["*"];
|
| 201 | ```
|
| 202 |
|
| 203 | You can destructure the `*`, you just have to assign it a new name. A common name is `splat`:
|
| 204 |
|
| 205 | ```tsx
|
| 206 | let { "*": splat } = useParams();
|
| 207 | ```
|
| 208 |
|
| 209 | ## Linking
|
| 210 |
|
| 211 | Link to routes from your UI with `Link` and `NavLink`
|
| 212 |
|
| 213 | ```tsx
|
| 214 | import { NavLink, Link } from "react-router";
|
| 215 |
|
| 216 | function Header() {
|
| 217 | return (
|
| 218 | <nav>
|
| 219 | {/* NavLink makes it easy to show active states */}
|
| 220 | <NavLink
|
| 221 | to="/"
|
| 222 | className={({ isActive }) =>
|
| 223 | isActive ? "active" : ""
|
| 224 | }
|
| 225 | >
|
| 226 | Home
|
| 227 | </NavLink>
|
| 228 |
|
| 229 | <Link to="/concerts/salt-lake-city">Concerts</Link>
|
| 230 | </nav>
|
| 231 | );
|
| 232 | }
|
| 233 | ```
|
| 234 |
|
| 235 | ---
|
| 236 |
|
| 237 | Next: [Navigating](./navigating)
|