Skip to content

Commit 3f523e5

Browse files
Add support for routes.ts (#11773)
1 parent 8540316 commit 3f523e5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1547
-353
lines changed

docs/start/installation.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,12 @@ export default function Home() {
8888
```
8989

9090
```ts filename=app/routes.ts
91-
import { route } from "@react-router/dev/routes";
91+
import {
92+
type RoutesConfig,
93+
index,
94+
} from "@react-router/dev/routes";
9295

93-
export const routes = createRoutes([
94-
route.index("./home.tsx"),
95-
]);
96+
export const routes: RoutesConfig = [index("./home.tsx")];
9697
```
9798

9899
```tsx filename=vite.config.ts

docs/start/routing.md

+67-52
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,24 @@ order: 2
1010
Routes are configured in `app/routes.ts`. The Vite plugin uses this file to create bundles for each route.
1111

1212
```ts filename=app/routes.ts
13-
import { route } from "react-router/config";
14-
15-
export default [
16-
route.index("./home.tsx"),
13+
import {
14+
type RoutesConfig,
15+
route,
16+
index,
17+
layout,
18+
} from "@react-router/dev/routes";
19+
20+
export const routes: RoutesConfig = [
21+
index("./home.tsx"),
1722
route("about", "./about.tsx"),
1823

19-
route.layout("./auth/layout.tsx", [
24+
layout("./auth/layout.tsx", [
2025
route("login", "./auth/login.tsx"),
2126
route("register", "./auth/register.tsx"),
2227
]),
2328

2429
route("concerts", [
25-
route.index("./concerts/home.tsx"),
30+
index("./concerts/home.tsx"),
2631
route(":city", "./concerts/city.tsx"),
2732
route("trending", "./concerts/trending.tsx"),
2833
]),
@@ -31,19 +36,29 @@ export default [
3136

3237
## File System Routes
3338

34-
If you prefer a file system routing convention there are two conventions from React Router, but you can also make your own.
39+
If you prefer a file system routing convention, you can use the convention provided with Remix v2, but you can also make your own.
3540

3641
```tsx filename=app/routes.ts
37-
import { nested, flat } from "@react-router/fs-routes";
42+
import { type RoutesConfig } from "@react-router/dev/routes";
43+
import { remixRoutes } from "@react-router/remix-v2-routes";
44+
45+
export const routes: RoutesConfig = remixRoutes();
46+
```
3847

39-
export const routes = [
40-
// simulates Next.js route file names
41-
...nested("./routes"),
48+
You can also mix routing conventions into a single array of routes.
49+
50+
```tsx filename=app/routes.ts
51+
import {
52+
type RoutesConfig,
53+
route,
54+
} from "@react-router/dev/routes";
55+
import { remixRoutes } from "@react-router/remix-v2-routes";
4256

43-
// simulates Remix v2 route file names
44-
...flat("./routes"),
57+
export const routes: RoutesConfig = [
58+
// Provide Remix v2 file system routes
59+
...(await remixRoutes()),
4560

46-
// can still do regular configuration
61+
// Then provide additional config routes
4762
route("/can/still/add/more", "./more.tsx"),
4863
];
4964
```
@@ -76,10 +91,18 @@ function Header() {
7691
Routes can be nested inside parent routes. Nested routes are rendered into their parent's [Outlet][outlet]
7792

7893
```ts filename=app/routes.ts
79-
route("dashboard", "./dashboard.tsx", () => [
80-
route.index("./home.tsx"),
81-
route("settings", "./settings.tsx"),
82-
]);
94+
import {
95+
type RoutesConfig,
96+
route,
97+
index,
98+
} from "@react-router/dev/routes";
99+
100+
export const routes: RoutesConfig = [
101+
route("dashboard", "./dashboard.tsx", [
102+
index("./home.tsx"),
103+
route("settings", "./settings.tsx"),
104+
]),
105+
];
83106
```
84107

85108
```tsx filename=app/dashboard.tsx
@@ -100,41 +123,52 @@ export default defineRoute$({
100123

101124
## Layout Routes
102125

103-
Using `route.layout`, layout routes create new nesting for their children, but they don't add any segments to the URL. They can be added at any level.
126+
Using `layout`, layout routes create new nesting for their children, but they don't add any segments to the URL. They can be added at any level.
127+
128+
```tsx filename=app/routes.ts lines=[9,15]
129+
import {
130+
type RoutesConfig,
131+
route,
132+
layout,
133+
index,
134+
} from "@react-router/dev/routes";
104135

105-
```tsx filename=app/routes.ts lines=[3,9]
106-
import { route } from "react-router/config";
107-
export const routes = [
108-
route.layout("./marketing/layout.tsx", [
109-
route.index("./marketing/home.tsx"),
136+
export const routes: RoutesConfig = [
137+
layout("./marketing/layout.tsx", [
138+
index("./marketing/home.tsx"),
110139
route("contact", "./marketing/contact.tsx"),
111140
]),
112141
route("projects", [
113-
route.index("./projects/home.tsx"),
114-
route.layout("./projects/project-layout.tsx", [
142+
index("./projects/home.tsx"),
143+
layout("./projects/project-layout.tsx", [
115144
route(":pid", "./projects/project.tsx"),
116145
route(":pid/edit", "./projects/edit-project.tsx"),
117146
]),
118147
]),
119-
]);
148+
];
120149
```
121150

122151
## Index Routes
123152

124153
```ts
125-
route.index(componentFile);
154+
index(componentFile);
126155
```
127156

128157
Index routes render into their parent's [Outlet][outlet] at their parent's URL (like a default child route).
129158

130159
```ts filename=app/routes.ts
131-
import { route } from "react-router/config";
132-
export default [
160+
import {
161+
type RoutesConfig,
162+
route,
163+
index,
164+
} from "@react-router/dev/routes";
165+
166+
export const routes: RoutesConfig = [
133167
// renders into the root.tsx Outlet at /
134-
route.index("./home.tsx"),
135-
route("dashboard", "./dashboard.tsx", () => [
168+
index("./home.tsx"),
169+
route("dashboard", "./dashboard.tsx", [
136170
// renders into the dashboard.tsx Outlet at /dashboard
137-
route.index("./dashboard-home.tsx"),
171+
index("./dashboard-home.tsx"),
138172
route("settings", "./dashboard-settings.tsx"),
139173
]),
140174
];
@@ -222,25 +256,6 @@ You can destructure the `*`, you just have to assign it a new name. A common nam
222256
const { "*": splat } = params;
223257
```
224258

225-
## Case Sensitive Routes
226-
227-
You can make your routes case sensitive by exporting a `config` object from `app/routes.ts`:
228-
229-
```ts filename=app/routes.ts
230-
import { route } from "react-router/config";
231-
232-
export const config = {
233-
caseSensitive: true,
234-
};
235-
236-
export default [
237-
route("wEll-aCtuAlly", "./well-actually.tsx"),
238-
];
239-
```
240-
241-
- Will match `"wEll-aCtuAlly"`
242-
- Will not match `"well-actually"`
243-
244259
## Component Routes
245260

246261
You can also use components that match the URL to elements anywhere in the component tree:

docs/upgrading/vite-component-routes.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -176,14 +176,14 @@ To get back to rendering your app, we'll configure a "catchall" route that match
176176
Create a file at `src/routes.ts` and add this:
177177

178178
```ts filename=src/routes.ts
179-
import { defineRoutes } from "react-router/config";
179+
import { type RoutesConfig } from "@react-router/dev/routes";
180180

181-
export default defineRoutes([
181+
export const routes: RoutesConfig = [
182182
{
183183
path: "*",
184184
file: "src/catchall.tsx",
185185
},
186-
]);
186+
];
187187
```
188188

189189
And then create the catchall route module and render your existing root App component within it.
@@ -221,9 +221,9 @@ export default function App() {
221221
You can move the definition to a `routes.ts` file:
222222

223223
```tsx filename=src/routes.ts
224-
import { defineRoutes } from "react-router/config";
224+
import { type RoutesConfig } from "@react-router/dev/routes";
225225

226-
export default defineRoutes([
226+
export const routes: RoutesConfig = [
227227
{
228228
path: "/pages/:id",
229229
file: "./containers/page.tsx",
@@ -232,7 +232,7 @@ export default defineRoutes([
232232
path: "*",
233233
file: "src/catchall.tsx",
234234
},
235-
]);
235+
];
236236
```
237237

238238
And then edit the route module to use the Route Module API:

docs/upgrading/vite-router-provider.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -197,14 +197,14 @@ ReactDOM.hydrateRoot(
197197
You can move the definition to a `routes.ts` file:
198198

199199
```tsx filename=src/routes.ts
200-
import { defineRoutes } from "react-router/config";
200+
import { type RoutesConfig } from "@react-router/dev/routes";
201201

202-
export default defineRoutes([
202+
export const routes: RoutesConfig = [
203203
{
204204
path: "/pages/:id",
205205
file: "./containers/page.tsx",
206206
},
207-
]);
207+
];
208208
```
209209

210210
And then edit the route module to use the Route Module API:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { type RoutesConfig } from "@react-router/dev/routes";
2+
import { remixRoutes } from "@react-router/remix-v2-routes";
3+
4+
export const routes: RoutesConfig = remixRoutes();

integration/helpers/node-template/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
},
2323
"devDependencies": {
2424
"@react-router/dev": "workspace:*",
25+
"@react-router/remix-v2-routes": "workspace:*",
2526
"@types/react": "^18.2.20",
2627
"@types/react-dom": "^18.2.7",
2728
"@vanilla-extract/css": "^1.10.0",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { type RoutesConfig } from "@react-router/dev/routes";
2+
import { remixRoutes } from "@react-router/remix-v2-routes";
3+
4+
export const routes: RoutesConfig = remixRoutes();

integration/helpers/vite-cloudflare-template/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"devDependencies": {
2222
"@cloudflare/workers-types": "^4.20230518.0",
2323
"@react-router/dev": "workspace:*",
24+
"@react-router/remix-v2-routes": "workspace:*",
2425
"@types/react": "^18.2.20",
2526
"@types/react-dom": "^18.2.7",
2627
"typescript": "^5.1.6",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { type RoutesConfig } from "@react-router/dev/routes";
2+
import { remixRoutes } from "@react-router/remix-v2-routes";
3+
4+
export const routes: RoutesConfig = remixRoutes();

integration/helpers/vite-template/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
},
2626
"devDependencies": {
2727
"@react-router/dev": "workspace:*",
28+
"@react-router/remix-v2-routes": "workspace:*",
2829
"@types/react": "^18.2.20",
2930
"@types/react-dom": "^18.2.7",
3031
"eslint": "^8.38.0",

integration/flat-routes-test.ts renamed to integration/remix-v2-routes-test.ts

+34-8
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,30 @@ import {
1313
let fixture: Fixture;
1414
let appFixture: AppFixture;
1515

16-
test.describe("flat routes", () => {
17-
let IGNORED_ROUTE = "ignore-me-pls";
16+
test.describe("remix v2 routes", () => {
1817
test.beforeAll(async () => {
1918
fixture = await createFixture({
2019
files: {
21-
"vite.config.js": `
20+
"vite.config.js": js`
2221
import { defineConfig } from "vite";
2322
import { reactRouter } from "@react-router/dev/vite";
2423
2524
export default defineConfig({
26-
plugins: [reactRouter({
27-
ignoredRouteFiles: [${JSON.stringify(`**/${IGNORED_ROUTE}.*`)}],
28-
})],
25+
plugins: [reactRouter()],
26+
});
27+
`,
28+
"app/routes.ts": js`
29+
import { type RoutesConfig } from "@react-router/dev/routes";
30+
import { remixRoutes } from "@react-router/remix-v2-routes";
31+
32+
export const routes: RoutesConfig = remixRoutes({
33+
ignoredRouteFiles: ["**/ignored-route.*"],
34+
routes: async (defineRoutes) => {
35+
// Ensure async routes work
36+
return defineRoutes((route) => {
37+
route("/custom/route", "custom-route.tsx")
38+
});
39+
}
2940
});
3041
`,
3142
"app/root.tsx": js`
@@ -74,6 +85,12 @@ test.describe("flat routes", () => {
7485
}
7586
`,
7687

88+
"app/custom-route.tsx": js`
89+
export default function () {
90+
return <h2>Custom Route</h2>;
91+
}
92+
`,
93+
7794
"app/routes/.dotfile": `
7895
DOTFILE SHOULD BE IGNORED
7996
`,
@@ -107,7 +124,7 @@ test.describe("flat routes", () => {
107124
}
108125
`,
109126

110-
[`app/routes/${IGNORED_ROUTE}.jsx`]: js`
127+
[`app/routes/ignored-route.jsx`]: js`
111128
export default function () {
112129
return <h2>i should 404</h2>;
113130
}
@@ -169,6 +186,15 @@ test.describe("flat routes", () => {
169186
</div>`);
170187
});
171188

189+
test("renders matching routes (custom route)", async ({ page }) => {
190+
let app = new PlaywrightFixture(appFixture, page);
191+
await app.goto("/custom/route");
192+
expect(await app.getHtml("#content")).toBe(`<div id="content">
193+
<h1>Root</h1>
194+
<h2>Custom Route</h2>
195+
</div>`);
196+
});
197+
172198
test("renders matching routes (route with escaped leading dot)", async ({
173199
page,
174200
}) => {
@@ -192,7 +218,7 @@ test.describe("flat routes", () => {
192218
}
193219

194220
test("allows ignoredRouteFiles to be configured", async () => {
195-
let response = await fixture.requestDocument("/" + IGNORED_ROUTE);
221+
let response = await fixture.requestDocument("/ignored-route");
196222

197223
expect(response.status).toBe(404);
198224
});

0 commit comments

Comments
 (0)