Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RemixBrowser routes prop #9062

Open
wants to merge 7 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/remix-browser-routes.md
@@ -0,0 +1,7 @@
---
"@remix-run/dev": minor
"@remix-run/react": minor
"@remix-run/server-runtime": minor
---

Add new `<RemixBrowser routes>` prop to permit client-side route definitions in SPA mode
53 changes: 53 additions & 0 deletions docs/file-conventions/entry.client.md
Expand Up @@ -28,4 +28,57 @@ startTransition(() => {

This is the first piece of code that runs in the browser. You can initialize client side libraries, add client only providers, etc.

## `RemixBrowser`

The `RemixBrowser` component is the top-level component of your Remix application - and will render from the [root component][root] down for the matched routes.

### `routes` prop

`RemixBrowser` accepts a single optional `routes` prop that can be used with [Remix SPA Mode][spa-mode] if you have not yet moved your routes to use the [file-based routing convention][file-based-routing] or the [`routes`][routes] config. The routes passed via this prop will be appended as additional children of your root route.

Routes defined this way are strictly client-side, so your `loader`/`action` will be internally converted to Remix `clientLoader`/`clientAction`'s.

<docs-info>This is not intended to be used as a primary method of definin routes, and is intended to be used as a migration path from a React Router `RouterProvider` application.</docs-info>

<docs-warn>If any collisions are detected from routes on the file system then a warning will be logged and the `routes` prop will be ignored.</docs-warn>

```tsx filename=entry.client.stsx
import { RemixBrowser } from "@remix-run/react";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";

const routes = [
{
index: true,
loader: indexLoader,
Component: Index,
},
{
path: "/parent",
loader: parentLoader,
Component: Parent,
children: [
{
path: "child",
loader: childLoader,
Component: Child,
},
],
},
];

startTransition(() => {
hydrateRoot(
document,
<StrictMode>
<RemixBrowser routes={routes} />
</StrictMode>
);
});
```

[root]: ./root
[server_entry_module]: ./entry.server
[spa-mode]: ../future/spa-mode
[file-based-routing]: ./routes
[routes]: ./vite-config#routes
16 changes: 8 additions & 8 deletions docs/future/spa-mode.md
Expand Up @@ -270,17 +270,16 @@ Once you're using vite, you should be able to drop your `BrowserRouter` app into

**If you are currently using `RouterProvider`**

If you are currently using `RouterProvider`, then the best approach is to move your routes to individual files and load them via `route.lazy`:
Replace your React Router `index.html` file with an `app/root.tsx` route that exports a `default` component (which renders an `Outlet`) and `HydrateFallback` for your loading state.

- Name these files according to the Remix file conventions to make the move to Remix (SPA) easier
- Export your route components as a named `Component` export (for RR) and also a `default` export (for eventual use by Remix)
You can migrate your routes by passing your route config to the [`<RemixBrowser routes>`][remix-browser-routes] prop, which should get your current `RouterProvider` app running in Remix SPA Mode.

Once you've got all your routes living in their own files, you can:
Then, you can start moving sub-trees to individual files iteratively:

- Move those files over into the Remix `app/` directory
- Enable SPA Mode
- Rename all `loader`/`action` function to `clientLoader`/`clientAction`
- Replace your React Router `index.html` file with an `app/root.tsx` route that exports a `default` component and `HydrateFallback`
- Export your route `Component` as the `default` export
- Rename any `loader`/`action` functions to `clientLoader`/`clientAction`

You must move entire sub-trees starting with top-level routes, since Remix doesn't know how to intelligently combine your file-based routes with prop-based routes. I.e., you can't have `routes/parent.tsx` and then provide a `path: "child"` route via the `<RemixBrowser routes>` prop that is intended to be a child of the parent route. You must move the parent route and all of it's children to files in one pass.

[rfc]: https://github.com/remix-run/remix/discussions/7638
[client-data]: ../guides/client-data
Expand All @@ -302,3 +301,4 @@ Once you've got all your routes living in their own files, you can:
[sirv-cli]: https://www.npmjs.com/package/sirv-cli
[vite-ssr-noexternal]: https://vitejs.dev/config/ssr-options#ssr-noexternal
[vite-ssr-external]: https://vitejs.dev/config/ssr-options#ssr-external
[remix-browser-routes]: ../file-conventions/entry.client#routes-prop