Skip to content

Commit a02996d

Browse files
committed
docs: bring your own bundler
1 parent 65c0e3b commit a02996d

File tree

1 file changed

+174
-0
lines changed

1 file changed

+174
-0
lines changed

docs/start/framework/byob.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
---
2+
title: Bring Your Own Bundler
3+
---
4+
5+
<docs-warning>This document is not complete and may contain errors</docs-warning>
6+
7+
# Bring Your Own Bundler
8+
9+
The framework features are enabled by runtime features of React React. Instead of using React Router's Vite plugin, you can bring your own bundler and server abstractions.
10+
11+
## Client Rendering
12+
13+
### 1. Create a Router
14+
15+
The browser runtime API that enables route module APIs (loaders, actions, etc.) is `createBrowserRouter`.
16+
17+
It takes an array of route objects that support loaders, actions, error boundaries and more. The React Router Vite plugin creates one of these from `routes.ts`, but you can create one manually (or with an abstraction) and use your own bundler.
18+
19+
```tsx
20+
import { createBrowserRouter } from "react-router";
21+
22+
let router = createBrowserRouter([
23+
{
24+
path: "/",
25+
Component: Root,
26+
children: [
27+
{
28+
path: "shows/:showId",
29+
Component: Show,
30+
loader: ({ request, params }) =>
31+
fetch(`/api/show/${params.id}.json`, {
32+
signal: request.signal,
33+
}),
34+
},
35+
],
36+
},
37+
]);
38+
```
39+
40+
### 2. Render the Router
41+
42+
To render the router in the browser, use `<RouterProvider>`.
43+
44+
```tsx
45+
import {
46+
createBrowserRouter,
47+
RouterProvider,
48+
} from "react-router";
49+
import { createRoot } from "react-dom/client";
50+
51+
createRoot(document.getElementById("root")).render(
52+
<RouterProvider router={router} />
53+
);
54+
```
55+
56+
### 3. Lazy Loading
57+
58+
Routes can take most of their definition lazily with the `lazy` property.
59+
60+
```tsx
61+
createBrowserRouter([
62+
{
63+
path: "/show/:showId",
64+
lazy: () => {
65+
let [loader, action, Component] = await Promise.all([
66+
import("./show.action.js"),
67+
import("./show.loader.js"),
68+
import("./show.component.js"),
69+
]);
70+
return { loader, action, Component };
71+
},
72+
},
73+
]);
74+
```
75+
76+
## Server Rendering
77+
78+
To server render a custom setup, there are a few server APIs available for rendering an data loading.
79+
80+
### 1. Define Your Routes
81+
82+
Routes are the same kinds of objects on the server as the client.
83+
84+
```tsx
85+
export default [
86+
{
87+
path: "/",
88+
Component: Root,
89+
children: [
90+
{
91+
path: "shows/:showId",
92+
Component: Show,
93+
loader: ({ params }) => {
94+
return db.loadShow(params.id);
95+
},
96+
},
97+
],
98+
},
99+
];
100+
```
101+
102+
### 2. Create a static handler
103+
104+
Turn your routes into a request handler with `createStaticHandler`:
105+
106+
```tsx
107+
import { createStaticHandler } from "react-router";
108+
import routes from "./some-routes";
109+
110+
let { query, dataRoutes } = createStaticHandler(routes);
111+
```
112+
113+
### 3. Get Routing Context and Render
114+
115+
React Router works with web fetch [Requests](https://developer.mozilla.org/en-US/docs/Web/API/Request), so if your server doesn't, you'll need to adapt whatever objects it uses to a web fetch `Request` object.
116+
117+
This step assumes your server receives `Request` objects.
118+
119+
```tsx
120+
import { renderToString } from "react-dom/server";
121+
import {
122+
createStaticHandler,
123+
createStaticRouter,
124+
StaticRouterProvider,
125+
} from "react-router";
126+
127+
import routes from "./some-routes.js";
128+
129+
let { query, dataRoutes } = createStaticHandler(routes);
130+
131+
export async function handler(request: Request) {
132+
// 1. run actions/loaders to get the routing context with `query`
133+
let context = await query(request);
134+
135+
// If `query` returns a Response, send it raw (a route probably a redirected)
136+
if (context instanceof Response) {
137+
return context;
138+
}
139+
140+
// 2. Create a static router for SSR
141+
let router = createStaticRouter(dataRoutes, context);
142+
143+
// 3. Render everything with StaticRouterProvider
144+
let html = renderToString(
145+
<StaticRouterProvider
146+
router={router}
147+
context={context}
148+
/>
149+
);
150+
151+
// Setup headers from action and loaders from deepest match
152+
let leaf = context.matches[context.matches.length - 1];
153+
let actionHeaders = context.actionHeaders[leaf.route.id];
154+
let loaderHeaders = context.loaderHeaders[leaf.route.id];
155+
let headers = new Headers(actionHeaders);
156+
if (loaderHeaders) {
157+
for (let [key, value] of loaderHeaders.entries()) {
158+
headers.append(key, value);
159+
}
160+
}
161+
162+
headers.set("Content-Type", "text/html; charset=utf-8");
163+
164+
// 4. send a response
165+
return new Response(`<!DOCTYPE html>${html}`, {
166+
status: context.statusCode,
167+
headers,
168+
});
169+
}
170+
```
171+
172+
### 4. Hydrate in the browser
173+
174+
This section is incomplete and will be updated, please refer to `HydratedRouter` source to see how React Router does this with the React Router Vite plugin.

0 commit comments

Comments
 (0)