A React framework for building full-stack, server-driven web applications on Cloudflare's edge platform with SSR, RSC, and realtime features
npx @tessl/cli install tessl/npm-rwsdk@1.0.0RedwoodSDK is a comprehensive React framework for building full-stack, server-driven web applications on Cloudflare's edge platform. It provides seamless integration with Vite for development, enables Server-Side Rendering (SSR) and React Server Components (RSC), offers a composable router with middleware capabilities, and includes realtime features via WebSockets and Durable Objects. The framework is specifically designed for Cloudflare Workers and Durable Objects, providing type-safe APIs for authentication, database access, state synchronization, and AI/LLM integrations.
npm install rwsdkvite ^6.2.6 || 7.xreact >=19.2.0-0 <20.0.0react-dom >=19.2.0-0 <20.0.0react-server-dom-webpack >=19.2.0-0 <20.0.0@cloudflare/vite-plugin ^1.13.10wrangler ^4.35.0capnweb ~0.2.0// Vite plugin
import { redwood } from 'rwsdk/vite';
// Worker runtime
import { defineApp, route, render, defineScript } from 'rwsdk/worker';
// Client runtime
import { initClient, navigate, ClientOnly } from 'rwsdk/client';
// Router (server-side)
import { route, prefix, layout, defineRoutes } from 'rwsdk/router';
// Authentication
import { defineSessionStore, defineDurableSession } from 'rwsdk/auth';
// Database
import { createDb, SqliteDurableObject, sql } from 'rwsdk/db';
// Realtime
import { realtimeRoute, renderRealtimeClients } from 'rwsdk/realtime/worker';
import { initRealtimeClient } from 'rwsdk/realtime/client';
import { RealtimeDurableObject } from 'rwsdk/realtime/durableObject';1. Configure Vite plugin (vite.config.ts):
import { defineConfig } from 'vite';
import { redwood } from 'rwsdk/vite';
export default defineConfig({
plugins: [await redwood()],
});2. Define worker entry point (worker.tsx):
import { defineApp, route, render } from 'rwsdk/worker';
import HomePage from './pages/HomePage';
function Document({ children }) {
return (
<html>
<head>
<title>My App</title>
</head>
<body>{children}</body>
</html>
);
}
export default defineApp([
render(Document, [
route('/', HomePage),
route('/api/hello', () => new Response('Hello, World!')),
]),
]);3. Create client entry (client.tsx):
import { initClient } from 'rwsdk/client';
initClient();4. Use React Server Components:
// users.ts (server function)
'use server';
import { db } from './db';
export async function getUsers() {
return await db.selectFrom('users').selectAll().execute();
}
// UserList.tsx (React Server Component)
import { getUsers } from './users';
export default async function UserList() {
const users = await getUsers();
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}RedwoodSDK is structured around several key layers:
Vite plugin for integrating RedwoodSDK into the build process, handling React Server Components transformation, code splitting between client and server bundles, and Cloudflare-specific optimizations.
function redwood(options?: RedwoodPluginOptions): Promise<InlineConfig["plugins"]>;
interface RedwoodPluginOptions {
silent?: boolean;
rootDir?: string;
includeCloudflarePlugin?: boolean;
includeReactPlugin?: boolean;
configPath?: string;
forceClientPaths?: string[];
forceServerPaths?: string[];
entry?: { worker?: string };
}Server-side runtime for Cloudflare Workers including application definition, rendering, React Server Components support, and request context management.
function defineApp<T, Routes>(routes: Routes): AppDefinition<Routes, T>;
function renderToStream(
element: React.ReactElement,
options?: RenderToStreamOptions
): Promise<ReadableStream>;
function renderToString(
element: React.ReactElement,
options?: RenderToStringOptions
): Promise<string>;
function getRequestInfo<Params = unknown, AppContext = DefaultAppContext>():
RequestInfo<Params, AppContext>;Client-side runtime for hydration, navigation, and React Server Components client integration.
function initClient(options?: {
transport?: Transport;
hydrateRootOptions?: HydrationOptions;
handleResponse?: (response: Response) => boolean;
}): void;
function navigate(
href: string,
options?: {
history?: "push" | "replace";
info?: { scrollToTop?: boolean; scrollBehavior?: string };
}
): void;Composable routing system with pattern matching, middleware, layouts, and type-safe link generation for both server and client environments.
function route<Path, T>(
path: Path,
handler: RouteHandler<T>
): RouteDefinition<Path, T>;
function prefix<Prefix, T, Routes>(
prefixPath: Prefix,
routes: Routes
): Route<T>;
function layout<T, Routes>(
LayoutComponent: React.ComponentType<LayoutProps<T>>,
routes: Routes
): Route<T>;
function defineLinks<App | Routes>(): LinkBuilder;Session management system supporting custom storage backends and Durable Object-based sessions with cookie handling and signature validation.
function defineSessionStore<Session, SessionInputData>(options: {
cookieName?: string;
secretKey?: string;
createCookie?: typeof createSessionCookie;
get: (sessionId: string) => Promise<Session>;
set: (sessionId: string, data: SessionInputData) => Promise<void>;
unset: (sessionId: string) => Promise<void>;
}): SessionStore<Session, SessionInputData>;
function defineDurableSession<SessionDurableObject>(options: {
cookieName?: string;
secretKey?: string;
createCookie?: typeof createSessionCookie;
sessionDurableObject: DurableObjectNamespace<SessionDurableObject>;
}): SessionStore<any, any>;Type-safe database access using Kysely ORM with SQLite on Durable Objects, including migration support and schema inference from migration definitions.
function createDb<DatabaseType>(
durableObjectBinding: DurableObjectNamespace,
name?: string
): Kysely<DatabaseType>;
class SqliteDurableObject<T> {
constructor(
ctx: DurableObjectState,
env: any,
migrations: Record<string, Migration>,
migrationTableName?: string,
plugins?: KyselyPlugin[]
);
initialize(): Promise<void>;
kysely: Kysely<T>;
}WebSocket-based realtime functionality with Durable Objects for server-side coordination and selective client rendering.
function realtimeRoute(
getDurableObjectNamespace: (env: any) => DurableObjectNamespace<RealtimeDurableObject>
): Route<any>;
function renderRealtimeClients(options: {
durableObjectNamespace: DurableObjectNamespace<RealtimeDurableObject>;
key?: string;
include?: string[];
exclude?: string[];
}): Promise<void>;
function initRealtimeClient(options?: {
key?: string;
handleResponse?: (response: Response) => boolean;
}): void;Cross-client state synchronization using Durable Objects with React hooks for seamless state updates across connected clients.
function useSyncedState<T>(
initialValue: T,
key: string
): [T, (value: T | ((prev: T) => T)) => void];
function syncedStateRoutes(
getNamespace: (env: any) => DurableObjectNamespace<SyncedStateServer>,
options?: { basePath?: string; durableObjectName?: string }
): Route<any>[];Integration with Cloudflare Turnstile CAPTCHA for bot protection with React hooks and server-side verification.
function useTurnstile(siteKey: string): {
ref: RefObject<HTMLDivElement>;
challenge: () => Promise<string>;
};
function verifyTurnstileToken(options: {
token: string;
secretKey: string;
fetchFn?: typeof fetch;
}): Promise<boolean>;Comprehensive end-to-end testing utilities with Puppeteer integration for testing both development and deployed applications.
function testDevAndDeploy(
name: string,
testLogic: (resources: TestResources) => Promise<void>
): void;
function launchBrowser(
browserPath?: string,
headless?: boolean
): Promise<Browser>;
function setupPlaygroundEnvironment(options: SmokeTestOptions): Promise<TestResources>;Namespaced debug logging with environment variable filtering for development and debugging.
function debug(namespace: string): (...args: any[]) => void;SDK-internal state management for coordinating between runtime components.
function defineRwState<T>(key: string, initializer: () => T): T;
function getRwState<T>(key: string): T | undefined;
function setRwState<T>(key: string, value: T): void;const IS_DEV: boolean;AI code generation rules exported as vibe-rules for use with AI coding assistants.
const rules: PackageRuleItem[];interface RequestInfo<Params = unknown, AppContext = DefaultAppContext> {
request: Request;
params: Params;
context: AppContext;
env: any;
ctx: ExecutionContext;
}
interface DefaultAppContext {
[key: string]: any;
}interface DocumentProps<T = any> {
children: React.ReactNode;
context?: T;
}
interface LayoutProps<T = any> {
children: React.ReactNode;
context?: T;
}type Transport = (
context: TransportContext
) => Promise<ReadableStream<Uint8Array>>;
interface TransportContext {
request: Request;
serverAction?: { id: string; args: any[] };
}
interface ActionResponse<Result> {
result?: Result;
error?: Error;
}