React Router (RSC)
examples/example-react-router
uses Vite’s experimental React Server Components plugin with StyleX compiled by
@stylexjs/unplugin. The StyleX plugin runs before the RSC plugin so both
client and server bundles share the same aggregated CSS.
Vite configuration
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import rsc from '@react-router/dev/vite';
import stylex from '@stylexjs/unplugin';
export default defineConfig({
plugins: [
stylex.vite({ useCSSLayers: true }),
react(),
rsc(),
],
});
- Keep
stylex.vite()first to preserve Fast Refresh.
CSS entrypoint
Ensure that there is one CSS file that is imported from your root layout component or JS entry point.
CSS Link and Hot Reloading during development
Ensure that the virtual CSS file and script for hot reloading styles are part of your bundle to enable hot reloading during development.
The easiest way to do this is by using an encapsulated helper that can also be used to insert a link tag for your production CSS.
// src/DevStyleXInject.tsx
'use client';
import { useEffect } from 'react';
function DevStyleXInjectImpl() {
useEffect(() => {
if (import.meta.env.DEV) {
import('virtual:stylex:runtime');
}
}, []);
return <link rel="stylesheet" href="/virtual:stylex.css" />;
}
export function DevStyleXInject({ cssHref }: { cssHref: string }) {
return import.meta.env.DEV ? (
<DevStyleXInjectImpl />
) : (
cssHref && <link rel="stylesheet" href={cssHref} />
);
}
Render <DevStyleXInject /> in your HTML shell component.
// src/routes/root/client.tsx (excerpt)
import { DevStyleXInject } from './DevStyleXInject';
export function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<DevStyleXInject />
</head>
<body>{children}</body>
</html>
);
}