React Router (formerly Remix)
Start from CLI: Scaffold a full-stack app with React Router v7 ↗ and the Cloudflare Vite plugin for lightning-fast development.
npm create cloudflare@latest -- my-react-router-app --framework=react-router
yarn create cloudflare my-react-router-app --framework=react-router
pnpm create cloudflare@latest my-react-router-app --framework=react-router
Or just deploy: Create a full-stack app using React Router v7, with CI/CD and previews all set up for you.
React Router v7 ↗ is a full-stack React framework for building web applications. It combines with the Cloudflare Vite plugin to provide a first-class experience for developing, building and deploying your apps on Cloudflare.
-
Create a new project with the create-cloudflare CLI (C3)
Terminal window npm create cloudflare@latest -- my-react-router-app --framework=react-routerTerminal window yarn create cloudflare my-react-router-app --framework=react-routerTerminal window pnpm create cloudflare@latest my-react-router-app --framework=react-routerHow is this project set up?
Below is a simplified file tree of the project.
Directorymy-react-router-app
Directoryapp
Directoryroutes
- ...
- entry.server.ts
- root.tsx
- routes.ts
Directoryworkers
- app.ts
- react-router.config.ts
- vite.config.ts
- wrangler.jsonc
react-router.config.ts
is your React Router config file ↗. In this file:ssr
is set totrue
, meaning that your application will use server-side rendering.future.unstable_viteEnvironmentApi
is set totrue
to enable compatibility with the Cloudflare Vite plugin.
vite.config.ts
is your Vite config file ↗. The React Router and Cloudflare plugins are included in theplugins
array. The Cloudflare Vite plugin runs your server code in the Workers runtime, ensuring your local development environment is as close to production as possible.wrangler.jsonc
is your Worker config file. In this file:main
points to./workers/app.ts
. This is the entry file for your Worker. The default export includes afetch
handler, which delegates the request to React Router.- If you want to add bindings to resources on Cloudflare's developer platform, you configure them here.
-
Develop locally
After creating your project, run the following command in your project directory to start a local development server.
Terminal window npm run devTerminal window yarn run devTerminal window pnpm run devWhat's happening in local development?
This project uses React Router in combination with the Cloudflare Vite plugin. This means that your application runs in the Cloudflare Workers runtime, just like in production, and enables access to local emulations of bindings.
-
Deploy your project
Your project can be deployed to a
*.workers.dev
subdomain or a Custom Domain from your own machine or from any CI/CD system, including Cloudflare's own Workers Builds.The following command will build and deploy your project. If you are using CI, ensure you update your "deploy command" configuration appropriately.
Terminal window npm run deployTerminal window yarn run deployTerminal window pnpm run deploy
With bindings, your application can be fully integrated with the Cloudflare Developer Platform, giving you access to compute, storage, AI and more.
Once you have configured the bindings in the Wrangler configuration file, they are then available within context.cloudflare
in your loader or action functions:
export function loader({ context }: Route.LoaderArgs) { return { message: context.cloudflare.env.VALUE_FROM_CLOUDFLARE };}
export default function Home({ loaderData }: Route.ComponentProps) { return <Welcome message={loaderData.message} />;}
As you have direct access to your Worker entry file (workers/app.ts
), you can also add additional exports such as Durable Objects and Workflows
Example: Using Workflows
Here is an example of how to set up a simple Workflow in your Worker entry file.
import { createRequestHandler } from "react-router";import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';
declare global { interface CloudflareEnvironment extends Env {}}
type Env = { MY_WORKFLOW: Workflow;};
export class MyWorkflow extends WorkflowEntrypoint<Env> { override async run(event: WorkflowEvent<Params>, step: WorkflowStep) { await step.do("first step", async () => { return { output: "First step result" }; });
await step.sleep("sleep", "1 second");
await step.do("second step", async () => { return { output: "Second step result" }; });
return "Workflow output"; }}
const requestHandler = createRequestHandler( () => import("virtual:react-router/server-build"), import.meta.env.MODE);
export default { async fetch(request, env, ctx) { return requestHandler(request, { cloudflare: { env, ctx }, }); },} satisfies ExportedHandler<CloudflareEnvironment>;
Configure it in your Wrangler configuration file:
{ "workflows": [ { "name": "my-workflow", "binding": "MY_WORKFLOW", "class_name": "MyWorkflow" } ]}
[[workflows]]name = "my-workflow"binding = "MY_WORKFLOW"class_name = "MyWorkflow"
And then use it in your application:
export function action({ context }: Route.LoaderArgs) { const instance = await env.MY_WORKFLOW.create({ params: { "hello": "world" }) return { id: instance.id, details: instance.status() };}
With bindings, your application can be fully integrated with the Cloudflare Developer Platform, giving you access to compute, storage, AI and more.
Was this helpful?
- Resources
- API
- New to Cloudflare?
- Products
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- 2025 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark
-