Pular para o conteúdo

TanStack Start

Atualizado em Ver como Markdown

TanStack Start is a full-stack framework for building web applications with server-side rendering, streaming, server functions, and bundling.

TanStack Start Detected
Generated configuration
wrangler.jsonc
main: .output/server/index.mjs
wrangler.jsonc
assets: directory: .output/public
wrangler.jsonc
compatibility_flags: nodejs_compat
wrangler.jsonc
observability: enabled: true
Workers Deployed
Wrangler handles configuration automatically

Create a new application

Create a TanStack Start application pre-configured for Cloudflare Workers:


								
									
									npm
									 create cloudflare@latest -- my-tanstack-start-app --framework=tanstack-start
								
							

Start a local development server to preview your project during development:


								
									
									npm
									 run dev
								
							

Configure an existing application

If you have an existing TanStack Start application, configure it to run on Cloudflare Workers:

  1. Install @cloudflare/vite-plugin and wrangler:

    
    								
    									
    									npm
    									 i @cloudflare/vite-plugin wrangler -- -D
    								
    							
  2. Add the Cloudflare plugin to your Vite configuration:

    vite.config.jsjs
    import { defineConfig } from "vite";
    import { tanstackStart } from "@tanstack/react-start/plugin/vite";
    import { cloudflare } from "@cloudflare/vite-plugin";
    import react from "@vitejs/plugin-react";
    
    export default defineConfig({
    	plugins: [
    		cloudflare({ viteEnvironment: { name: "ssr" } }),
    		tanstackStart(),
    		react(),
    	],
    });
    vite.config.tsts
    import { defineConfig } from "vite";
    import { tanstackStart } from "@tanstack/react-start/plugin/vite";
    import { cloudflare } from "@cloudflare/vite-plugin";
    import react from "@vitejs/plugin-react";
    
    export default defineConfig({
    	plugins: [
    		cloudflare({ viteEnvironment: { name: "ssr" } }),
    		tanstackStart(),
    		react(),
    	],
    });
  3. Add a wrangler.jsonc configuration file:

    {
    	"$schema": "node_modules/wrangler/config-schema.json",
    	"name": "<YOUR_PROJECT_NAME>",
    	// Set this to today's date
    	"compatibility_date": "2026-07-20",
    	"compatibility_flags": ["nodejs_compat"],
    	"main": "@tanstack/react-start/server-entry",
    	"observability": {
    		"enabled": true,
    	},
    }
    "$schema" = "node_modules/wrangler/config-schema.json"
    name = "<YOUR_PROJECT_NAME>"
    # Set this to today's date
    compatibility_date = "2026-07-20"
    compatibility_flags = [ "nodejs_compat" ]
    main = "@tanstack/react-start/server-entry"
    
    [observability]
    enabled = true
  4. Update the scripts section in package.json:

    package.jsonjson
    {
    	"scripts": {
    		"dev": "vite dev",
    		"build": "vite build",
    		"preview": "vite preview",
    		"deploy": "npm run build && wrangler deploy",
    		"cf-typegen": "wrangler types"
    	}
    }

Deploy

Deploy to a *.workers.dev subdomain or a custom domain from your machine or any CI/CD system, including Workers Builds.


								
									
									npm
									 run deploy
								
							

Custom entrypoints

TanStack Start uses @tanstack/react-start/server-entry as your default entrypoint. Create a custom server entrypoint to add additional Workers handlers such as Queues and Cron Triggers. This is also where you can add additional exports such as Durable Objects and Workflows.

  1. Create a custom server entrypoint file:

    src/server.jsjs
    import handler from "@tanstack/react-start/server-entry";
    
    // Export Durable Objects as named exports
    export { MyDurableObject } from "./my-durable-object";
    
    export default {
    	fetch: handler.fetch,
    
    	// Handle Queue messages
    	async queue(batch, env, ctx) {
    		for (const message of batch.messages) {
    			console.log("Processing message:", message.body);
    			message.ack();
    		}
    	},
    
    	// Handle Cron Triggers
    	async scheduled(event, env, ctx) {
    		console.log("Cron triggered:", event.cron);
    	},
    };
    src/server.tsts
    import handler from "@tanstack/react-start/server-entry";
    
    // Export Durable Objects as named exports
    export { MyDurableObject } from "./my-durable-object";
    
    export default {
    	fetch: handler.fetch,
    
    	// Handle Queue messages
    	async queue(batch, env, ctx) {
    		for (const message of batch.messages) {
    			console.log("Processing message:", message.body);
    			message.ack();
    		}
    	},
    
    	// Handle Cron Triggers
    	async scheduled(event, env, ctx) {
    		console.log("Cron triggered:", event.cron);
    	},
    };
  2. Update your Wrangler configuration to point to your custom entrypoint:

    {
    	"main": "src/server.ts",
    }
    main = "src/server.ts"

Test scheduled handlers locally

Test your scheduled handler locally using the /cdn-cgi/handler/scheduled endpoint:

curl "http://localhost:3000/cdn-cgi/handler/scheduled?cron=*+*+*+*+*"

Example: Using Workflows

Export a Workflow class from your custom entrypoint to run durable, multi-step tasks:

app/server.jsjs
import {
	WorkflowEntrypoint,
	WorkflowStep,
	WorkflowEvent,
} from "cloudflare:workers";

export class MyWorkflow extends WorkflowEntrypoint {
	async run(event, step) {
		const result = await step.do("process data", async () => {
			return `Processed: ${event.payload.input}`;
		});

		await step.sleep("wait", "10 seconds");

		await step.do("finalize", async () => {
			console.log("Workflow complete:", result);
		});
	}
}
app/server.tsts
import {
	WorkflowEntrypoint,
	WorkflowStep,
	WorkflowEvent,
} from "cloudflare:workers";

export class MyWorkflow extends WorkflowEntrypoint<Env> {
	async run(event: WorkflowEvent<{ input: string }>, step: WorkflowStep) {
		const result = await step.do("process data", async () => {
			return `Processed: ${event.payload.input}`;
		});

		await step.sleep("wait", "10 seconds");

		await step.do("finalize", async () => {
			console.log("Workflow complete:", result);
		});
	}
}

Add the Workflow configuration to your Wrangler configuration:

{
	"workflows": [
		{
			"name": "my-workflow",
			"binding": "MY_WORKFLOW",
			"class_name": "MyWorkflow",
		},
	],
}
[[workflows]]
name = "my-workflow"
binding = "MY_WORKFLOW"
class_name = "MyWorkflow"

Example: Using Service Bindings

Add a service binding to call another Worker's RPC methods from your TanStack Start application:

{
	"services": [
		{
			"binding": "AUTH_SERVICE",
			"service": "auth-worker",
		},
	],
}
[[services]]
binding = "AUTH_SERVICE"
service = "auth-worker"

Call the bound Worker's methods from a server function:

app/routes/index.jsxjs
import { createServerFn } from "@tanstack/react-start";
import { env } from "cloudflare:workers";

const verifyUser = createServerFn()
	.inputValidator((token) => token)
	.handler(async ({ data: token }) => {
		const result = await env.AUTH_SERVICE.verify(token);
		return result;
	});
app/routes/index.tsxts
import { createServerFn } from "@tanstack/react-start";
import { env } from "cloudflare:workers";

const verifyUser = createServerFn()
	.inputValidator((token: string) => token)
	.handler(async ({ data: token }) => {
		const result = await env.AUTH_SERVICE.verify(token);
		return result;
	});

Bindings

Your TanStack Start application can be fully integrated with the Cloudflare Developer Platform, in both local development and in production, by using bindings.

Access bindings by importing the env object in your server-side code:

app/routes/index.jsxjs
import { createFileRoute } from "@tanstack/react-router";
import { createServerFn } from "@tanstack/react-start";
import { env } from "cloudflare:workers";

export const Route = createFileRoute("/")({
	loader: () => getData(),
	component: RouteComponent,
});

const getData = createServerFn().handler(() => {
	// Access bindings via env
	// For example: env.MY_KV, env.MY_BUCKET, env.AI, etc.
});

function RouteComponent() {
	// ...
}
app/routes/index.tsxts
import { createFileRoute } from "@tanstack/react-router";
import { createServerFn } from "@tanstack/react-start";
import { env } from "cloudflare:workers";

export const Route = createFileRoute("/")({
	loader: () => getData(),
	component: RouteComponent,
});

const getData = createServerFn().handler(() => {
	// Access bindings via env
	// For example: env.MY_KV, env.MY_BUCKET, env.AI, etc.
});

function RouteComponent() {
	// ...
}

Generate TypeScript types for your bindings based on your Wrangler configuration:


								
									
									npm
									 run cf-typegen
								
							

With bindings, your application can be fully integrated with the Cloudflare Developer Platform, giving you access to compute, storage, AI and more.

Bindings

Access to compute, storage, AI and more.

Use R2 in a server function

Add an R2 bucket binding to your Wrangler configuration:

{
	"r2_buckets": [
		{
			"binding": "MY_BUCKET",
			"bucket_name": "<YOUR_BUCKET_NAME>",
		},
	],
}
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "<YOUR_BUCKET_NAME>"

Access the bucket in a server function:

app/routes/index.jsxjs
import { createServerFn } from "@tanstack/react-start";
import { env } from "cloudflare:workers";

const uploadFile = createServerFn({ method: "POST" })
	.validator((data) => data)
	.handler(async ({ data }) => {
		await env.MY_BUCKET.put(data.key, data.content);
		return { success: true };
	});

const getFile = createServerFn()
	.validator((key) => key)
	.handler(async ({ data: key }) => {
		const object = await env.MY_BUCKET.get(key);
		return object ? await object.text() : null;
	});
app/routes/index.tsxts
import { createServerFn } from "@tanstack/react-start";
import { env } from "cloudflare:workers";

const uploadFile = createServerFn({ method: "POST" })
	.validator((data: { key: string; content: string }) => data)
	.handler(async ({ data }) => {
		await env.MY_BUCKET.put(data.key, data.content);
		return { success: true };
	});

const getFile = createServerFn()
	.validator((key: string) => key)
	.handler(async ({ data: key }) => {
		const object = await env.MY_BUCKET.get(key);
		return object ? await object.text() : null;
	});

Static prerendering

Prerender your application to static HTML at build time and serve as static assets.

vite.config.jsjs
import { defineConfig } from "vite";
import { cloudflare } from "@cloudflare/vite-plugin";
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
	plugins: [
		cloudflare({ viteEnvironment: { name: "ssr" } }),
		tanstackStart({
			prerender: {
				enabled: true,
			},
		}),
		react(),
	],
});
vite.config.tsts
import { defineConfig } from "vite";
import { cloudflare } from "@cloudflare/vite-plugin";
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
	plugins: [
		cloudflare({ viteEnvironment: { name: "ssr" } }),
		tanstackStart({
			prerender: {
				enabled: true,
			},
		}),
		react(),
	],
});

For more options, refer to TanStack Start static prerendering.

Prerendering data sources

To prerender with production data, use remote bindings.

In CI environments, environment variables or secrets may not be available during the build. To make them accessible:

  • Set CLOUDFLARE_INCLUDE_PROCESS_ENV=true in your CI environment and provide the required values as environment variables.
  • If using Workers Builds, update your build settings.