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

feat(plugin-react-router): React Router 7 / Remix support for Rsbuild #4186

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
093dd58
feat(react-router): react router
ScriptedAlchemy Dec 13, 2024
0ccc528
feat(react-router): react router
ScriptedAlchemy Dec 13, 2024
12cc2c2
feat(react-router): react router
ScriptedAlchemy Dec 13, 2024
72c1bf9
feat(react-router): react router
ScriptedAlchemy Dec 13, 2024
73856be
feat(react-router): react router
ScriptedAlchemy Dec 13, 2024
0f91655
feat(plugin-manifest): implement named chunk organization
ScriptedAlchemy Dec 13, 2024
2e6c827
feat(rsbuild-plugin): support async route chunk flush
ScriptedAlchemy Dec 13, 2024
79be5a5
chore(react-router): convert server to mjs
ScriptedAlchemy Dec 13, 2024
e348b2d
chore(react-router): convert server to mjs
ScriptedAlchemy Dec 13, 2024
0bea51b
chore(react-router): convert server to mjs
ScriptedAlchemy Dec 13, 2024
fc0cc8f
chore(react-router): convert server to mjs
ScriptedAlchemy Dec 14, 2024
3e9a816
feat(react-router): enhance SSR support and general configuration in …
ScriptedAlchemy Dec 14, 2024
c33a554
stash
ScriptedAlchemy Dec 14, 2024
4e6be44
chore(react-router): update type definitions and improve SSR handling
ScriptedAlchemy Dec 14, 2024
e94420b
fix(react-router): improve SSR handling and response management
ScriptedAlchemy Dec 14, 2024
b3f3048
chore(react-router): add cspell configuration for improved spell chec…
ScriptedAlchemy Dec 14, 2024
4921130
stash
ScriptedAlchemy Dec 17, 2024
be79012
stash
ScriptedAlchemy Dec 18, 2024
7fd88c9
refactor(react-router): remove deprecated entry files and update paths
ScriptedAlchemy Dec 18, 2024
bd33a02
chore: add templates
ScriptedAlchemy Dec 18, 2024
7708723
feat(react-router): add new loadRouteModule function for improved rou…
ScriptedAlchemy Dec 19, 2024
f318d3c
chore: add templates
ScriptedAlchemy Dec 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@
"*.svelte",
"template-lit-*/src/my-element.*",
"tsconfig.json",
"tsconfig.*.json"
"tsconfig.*.json",
"**/dist/**",
"**/public/js/**",
"**/build/**",
"**/.cache/**",
"**/.temp/**"
],
"ignoreUnknown": true
},
Expand Down
1 change: 1 addition & 0 deletions cspell.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module.exports = {
'node_modules',
'pnpm-lock.yaml',
'README.pt-BR.md',
'**/public/js/**',
],
flagWords: banWords,
dictionaries: ['dictionary'],
Expand Down
2 changes: 2 additions & 0 deletions examples/react-router-sample/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/node_modules
/dist
23 changes: 23 additions & 0 deletions examples/react-router-sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# React Router Custom Framework

This completely-not-production-ready "framework" example shows how to integrate React Router with custom bundling and server abstractions instead of using `@react-router/dev`.

[React Router Docs](https://reactrouter.com)

## Running the app

```sh
pnpm i
pnpm start
```

## Goofing around with the app

```sh
pnpm i
pnpm dev
```

## Caveats

I whipped this together REALLY quickly, it certainly has errors and could be more thorough, but I hope it helps!
15 changes: 15 additions & 0 deletions examples/react-router-sample/app/about.loader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { data } from 'react-router';

export default async function load() {
await new Promise((resolve) => setTimeout(resolve, 200));

const isServer = typeof document === 'undefined';
const env = isServer ? 'server' : 'client';

return data(
{ message: `About loader from ${env} loader` },
{
headers: { 'X-Custom': 'Hello' },
},
);
}
85 changes: 85 additions & 0 deletions examples/react-router-sample/app/about.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
.about {
max-width: 800px;
margin: 0 auto;
font-family: var(--font-sans);
}

.title {
color: #0f172a;
margin: 0 0 1.5rem;
font-size: 3rem;
font-weight: 800;
letter-spacing: -0.025em;
line-height: 1.2;
background: linear-gradient(135deg, #0f172a 0%, #3b82f6 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}

.description {
color: #334155;
font-size: 1.25rem;
line-height: 1.6;
margin-bottom: 3rem;
letter-spacing: -0.01em;
}

.loaderDemo {
background: #f8fafc;
border-radius: 16px;
padding: 2rem;
border: 1px solid #e2e8f0;
box-shadow:
0 4px 6px -1px rgba(0, 0, 0, 0.1),
0 2px 4px -1px rgba(0, 0, 0, 0.06);
}

.loaderDemo h3 {
color: #3b82f6;
margin: 0 0 1.5rem;
font-size: 1.5rem;
font-weight: 700;
letter-spacing: -0.025em;
}

.message {
font-size: 1.125rem;
color: #0f172a;
margin: 0 0 1.5rem;
padding: 1.25rem;
background: white;
border-radius: 12px;
border-left: 4px solid #3b82f6;
box-shadow:
0 1px 3px 0 rgba(0, 0, 0, 0.1),
0 1px 2px 0 rgba(0, 0, 0, 0.06);
letter-spacing: -0.01em;
}

.note {
color: #64748b;
font-size: 1rem;
margin: 0;
font-style: italic;
line-height: 1.6;
letter-spacing: -0.01em;
}

@media (max-width: 640px) {
.title {
font-size: 2rem;
}

.description {
font-size: 1.125rem;
}

.loaderDemo {
padding: 1.5rem;
}

.message {
padding: 1rem;
}
}
25 changes: 25 additions & 0 deletions examples/react-router-sample/app/about.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useLoaderData } from 'react-router';
import type loader from './about.loader.js';
import styles from './about.module.css';

export default function About() {
const data = useLoaderData<typeof loader>();

return (
<div className={styles.about}>
<h2 className={styles.title}>About This Demo</h2>
<p className={styles.description}>
This demo showcases how Rsbuild can be used to create modern React
applications with server-side rendering and client-side hydration.
</p>
<div className={styles.loaderDemo}>
<h3>Loader Demo</h3>
<p className={styles.message}>{data.message}</p>
<p className={styles.note}>
This message was loaded using React Router's loader functionality,
demonstrating server/client data loading capabilities.
</p>
</div>
</div>
);
}
11 changes: 11 additions & 0 deletions examples/react-router-sample/app/context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createContext } from 'react';

export interface Assets {
scriptTags: string[];
styleTags: string[];
}

export const AssetsContext = createContext<Assets>({
scriptTags: [],
styleTags: [],
});
82 changes: 82 additions & 0 deletions examples/react-router-sample/app/home.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
.home {
text-align: center;
font-family: var(--font-sans);
}

.title {
color: #0f172a;
margin: 0 0 1.5rem;
font-size: 3rem;
font-weight: 800;
letter-spacing: -0.025em;
line-height: 1.2;
background: linear-gradient(135deg, #0f172a 0%, #3b82f6 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}

.description {
color: #334155;
font-size: 1.25rem;
line-height: 1.6;
margin-bottom: 3rem;
max-width: 42rem;
margin-left: auto;
margin-right: auto;
letter-spacing: -0.01em;
}

.features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
margin-top: 2rem;
}

.feature {
padding: 2rem;
background: #f8fafc;
border-radius: 16px;
transition: all 0.3s ease;
border: 1px solid #e2e8f0;
}

.feature:hover {
transform: translateY(-5px);
box-shadow:
0 10px 15px -3px rgba(0, 0, 0, 0.1),
0 4px 6px -2px rgba(0, 0, 0, 0.05);
border-color: #3b82f6;
background: rgba(59, 130, 246, 0.02);
}

.feature h3 {
color: #3b82f6;
margin: 0 0 1rem;
font-size: 1.5rem;
font-weight: 700;
letter-spacing: -0.025em;
}

.feature p {
color: #334155;
margin: 0;
font-size: 1.125rem;
line-height: 1.5;
letter-spacing: -0.01em;
}

@media (max-width: 640px) {
.title {
font-size: 2rem;
}

.description {
font-size: 1.125rem;
}

.feature {
padding: 1.5rem;
}
}
28 changes: 28 additions & 0 deletions examples/react-router-sample/app/home.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import styles from './home.module.css';
export const ServerAction = async () => {};

export default function Home() {
return (
<div className={styles.home}>
<h2 className={styles.title}>Welcome to Rsbuild</h2>
<p className={styles.description}>
This is a demo app showcasing Rsbuild with React Router and Server-Side
Rendering.
</p>
<div className={styles.features}>
<div className={styles.feature}>
<h3>Server-Side Rendering</h3>
<p>Full SSR support with hydration</p>
</div>
<div className={styles.feature}>
<h3>React Router</h3>
<p>Modern routing with data loading</p>
</div>
<div className={styles.feature}>
<h3>Asset Management</h3>
<p>Automatic CSS and JS handling</p>
</div>
</div>
</div>
);
}
26 changes: 26 additions & 0 deletions examples/react-router-sample/app/layout.client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { LoaderFunctionArgs } from 'react-router-dom';

export async function loader({ request }: LoaderFunctionArgs) {
const url = new URL(request.url);
const res = await fetch(url, {
headers: {
Accept: 'application/json',
},
});
return res.json();
}

export async function action({ request }: LoaderFunctionArgs) {
const url = new URL(request.url);
// call the server action
const res = await fetch(url, {
method: 'POST',
// @ts-expect-error this is valid, types are wrong
body: new URLSearchParams(await request.formData()),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
},
});
return res.json();
}
Loading