React Router v7
Add voidPlugin() alongside the React Router v7 Vite plugin to use Void's database, storage, and deployment features. You don't need a separate Cloudflare adapter.
Setup
1. Create a new project
npm create react-router@latest my-app
cd my-app2. Install dependencies
npm install -D void3. Configure vite.config.ts
import { defineConfig } from 'vite';
import { reactRouter } from '@react-router/dev/vite';
import { voidPlugin } from 'void';
export default defineConfig({
plugins: [
voidPlugin(), // must come before the framework plugin
reactRouter(),
],
});4. Deploy
void auth login
void deployUsing Void Platform Features
Most Void platform features work with React Router. Use them in loaders and actions:
Void-managed auth is not supported in framework mode yet. Use Better Auth's official React Router integration directly for now.
Database
import type { Route } from './+types/users';
import { db } from 'void/db';
import { users } from '@schema';
export async function loader({}: Route.LoaderArgs) {
return { users: await db.select().from(users) };
}KV Storage
import { kv } from 'void/kv';
export async function loader() {
return { settings: await kv.get('app:settings') };
}Blob Storage
import { storage } from 'void/storage';
export async function loader() {
return { avatar: await storage.get('avatars/user-1.png') };
}AI
import { ai } from 'void/ai';
export async function action() {
return ai.run('@cf/meta/llama-3.1-8b-instruct', {
prompt: 'Summarize the latest news',
});
}Cron Jobs
// crons/daily-cleanup.ts
import { defineScheduled } from 'void';
export const cron = '0 0 * * *';
export default defineScheduled(async () => {
// runs daily at midnight
});Queue Consumers
// queues/emails.ts
import { defineQueue } from 'void';
export default defineQueue<{ to: string; subject: string }>(async (batch) => {
for (const msg of batch.messages) {
// process each message
msg.ack();
}
});Environment Variables
Declare environment variables in env.ts, then read them with import { env } from "void/env". Void supplies types, checks values during build and deploy, and stops the build if client code references a server-only key. See Environment Variables.
React Router keeps loaders and components in the same route file. Void's client check therefore treats that file as reachable from the browser, even though React Router removes loaders from the client bundle. Read server secrets in a .server.ts companion module to keep that separation explicit.
Accessing Bindings Directly
You can also access Cloudflare bindings via the cloudflare:workers module:
import { env } from 'cloudflare:workers';
const { results } = await env.DB.prepare('SELECT * FROM users').all();