Skip to content
Merged
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 120 additions & 70 deletions src/content/docs/workers/framework-guides/web-apps/tanstack.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ sidebar:
order: 7
head: []
tags: ["full-stack"]
description: Create a TanStack Router application and deploy it to Cloudflare Workers with Workers Assets.
description: Create a TanStack Start application and deploy it to Cloudflare Workers with Workers Assets.
---

import { WranglerConfig, Steps, PackageManagers, Details } from "~/components";

## What is TanStack Start?

TanStack Start is a full-stack React framework powered by TanStack Router. It provides a full-document SSR, streaming, server functions, bundling, and more using tools like Nitro and Vite.
TanStack Start is a full-stack React framework powered by TanStack Router. It provides a full-document SSR, streaming, server functions, bundling, and more using Vite and modern web standards.

## Create a new TanStack Start

TanStack provides a start-basic project. We'll use this starter project to create a new TanStack Start application.
TanStack Start Beta has significantly improved Cloudflare compatibility compared to the Alpha version, making deployment and development much more straightforward.

<Steps>

Expand All @@ -27,10 +27,10 @@ TanStack provides a start-basic project. We'll use this starter project to creat
cd start-basic
npm install
```

<Details header="How is this project set up?">
This command will clone the TanStack Start basic project to your local machine, change directory to the project, and install the dependencies. TanStack [provides other examples](https://tanstack.com/start/latest/docs/framework/react/quick-start#examples) that you can use by replacing `start-basic` with the example you want to use.
</Details>
<Details header="How is this project set up?">
This command will clone the TanStack Start basic project to your local machine, change directory to the project, and install the dependencies. TanStack [provides other examples](https://tanstack.com/start/latest/docs/framework/react/quick-start#examples) that you can use by replacing `start-basic` with the example you want to use.
</Details>

2. **Develop locally**

Expand All @@ -46,34 +46,33 @@ Whether you created a new TanStack Start project or are using an existing projec

<Steps>

1. **Install `unenv` & `nitroCloudflareBindings` package**

[`unenv`](https://github.com/unjs/unenv) is a package that normalizes runtime environments across Node.js, browsers, and edge runtimes like Cloudflare Workers. It’s essential for TanStack Router because certain Node.js APIs are unavailable in the Workers environment. `unenv` offers compatible replacements for those APIs.

[`nitro-cloudflare-dev`](https://github.com/nitrojs/nitro-cloudflare-dev) enables access to the Cloudflare runtime bindings like R2, D1, and other Cloudflare services in the development server.

<PackageManagers pkg="unenv nitro-cloudflare-dev" />

2. **Modify the `app.config.ts` file**

To configure your application for Cloudflare Workers deployment, add the following lines to your `app.config.ts` file:

```ts
// Required imports
import { cloudflare } from 'unenv'
import nitroCloudflareBindings from "nitro-cloudflare-dev";

// Add this new server section to the defineConfig object
server: {
preset: "cloudflare-module",
unenv: cloudflare,
modules: [nitroCloudflareBindings],
},
1. **Configure Vite for Cloudflare compatibility**

Update your `vite.config.ts` file to use the `cloudflare-module` target for a compatible build:

```ts title="vite.config.ts" {14}
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import { defineConfig } from "vite";
import tsConfigPaths from "vite-tsconfig-paths";

export default defineConfig({
server: {
port: 3000,
},
plugins: [
tsConfigPaths({
projects: ["./tsconfig.json"],
}),
tanstackStart({
target: "cloudflare-module", // Key configuration for Cloudflare compatibility
}),
],
});
```

This will set the correct build format and runtime environment for Cloudflare.
This single configuration change is all that's needed to make your TanStack Start application compatible with Cloudflare Workers.

3. **Add a Wrangler file**
2. **Add a Wrangler file**

Create a `wrangler.jsonc` or `wrangler.toml` file in the root of your project, `wrangler.jsonc` is the recommended approach. This file is used to configure the Cloudflare Workers deployment.

Expand All @@ -82,23 +81,45 @@ Whether you created a new TanStack Start project or are using an existing projec
```json
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "start-basic",
"main": "./.output/server/index.mjs",
"compatibility_date": "2025-04-14",
"name": "my-start-app",
"main": ".output/server/index.mjs",
"compatibility_date": "$today",
"compatibility_flags": ["nodejs_compat"],
"assets": {
"directory": ".output/public"
},
"observability": {
"enabled": true
},
"assets": {
"directory": "./.output/public/"
},
"compatibility_flags": ["nodejs_compat"]
"kv_namespaces": [
{
"binding": "CACHE",
"id": "<Your KV ID>"
}
]
}
```

</WranglerConfig>

Note that the `directory` key is set to `.output/public/`, which is the folder that will be filled with the build output. Additionally, the `main` key is set to `.output/server/index.mjs`, indicating to Cloudflare Workers where to locate the entry point for your application.
Note that the `directory` key is set to `.output/public`, which is the folder that will be filled with the build output. Additionally, the `main` key is set to `.output/server/index.mjs`, indicating to Cloudflare Workers where to locate the entry point for your application. The `kv_namespaces` section shows an example of how to configure a KV namespace binding.

3. **Add deployment scripts to package.json**

Add the following scripts to your `package.json` file to streamline deployment and type generation:

```json title="package.json
{
"scripts": {
...
"deploy": "npm run build && wrangler deploy",
"cf-typegen": "wrangler types --env-interface Env"
}
}
```

The `deploy` script combines building and deploying in one command, while `cf-typegen` generates TypeScript types for your Cloudflare bindings.

4. **Build the application**

You must build your application before deploying it to Cloudflare Workers.
Expand All @@ -107,75 +128,104 @@ Whether you created a new TanStack Start project or are using an existing projec

5. **Deploy the application**

The command below will deploy your application to Cloudflare Workers and provide a deployment URL. Make sure to rebuild your application after making any changes to see those changes reflected in the deployment.
You can now use the deploy script to build and deploy your application in one command:

<PackageManagers type="run" args={"deploy"} />

Alternatively, you can still deploy directly with Wrangler:

```sh
npx wrangler deploy
```

When making changes in the future ensure you rebuild your application. The deploy will deploy what is in your `.output/public` folder and that only gets updated when you run the build command.

</Steps>

## Using Cloudflare Bindings

<Steps>

1. **Create a helper function to get access to Cloudflare bindings**
1. **Generate TypeScript types for your bindings**

Create a helper function named `cloudflareBindings.ts` in the `src/utils` folder, and paste in the below code. You can create a `utils` folder in your project if you don't already have one. The example assumes you have a KV namespace with a binding name of `CACHE` already created in your account and added to the wrangler file.
Before using Cloudflare bindings in your code, generate the TypeScript types to ensure proper type safety:

```ts
import type { KVNamespace } from "@cloudflare/workers-types";
<PackageManagers type="run" args={"cf-typegen"} />

This command reads your `wrangler.jsonc` configuration and generates an `Env` interface with all your configured bindings.

interface CloudflareBindings {
CACHE: KVNamespace;
2. **Create a helper function to get access to Cloudflare bindings**

Create a helper function named `bindings.ts` in the `src/utils` folder (create the folder if it doesn't exist), and paste in the below code. The example assumes you have a KV namespace with a binding name of `CACHE` already created in your account and added to the wrangler file.


```ts title="src/utils/bindings.ts"

let cachedEnv: Env | null = null;

// This gets called once at startup when running locally
const initDevEnv = async () => {
const { getPlatformProxy } = await import("wrangler");
const proxy = await getPlatformProxy();
cachedEnv = proxy.env as unknown as Env;
};

if (import.meta.env.DEV) {
await initDevEnv();
}

/**
* Will only work when being accessed on the server. Obviously, CF bindings are not available in the browser.
* @returns
*/
export async function getBindings() {
export function getBindings(): Env {
if (import.meta.env.DEV) {
const { getPlatformProxy } = await import("wrangler");
const { env } = await getPlatformProxy();
return env as unknown as CloudflareBindings;
if (!cachedEnv) {
throw new Error(
"Dev bindings not initialized yet. Call initDevEnv() first."
);
}
return cachedEnv;
}

return process.env as unknown as CloudflareBindings;
return process.env as unknown as Env;
}
```

<Details header="How is this code working?">
To ensure your bindings work locally with vinxi, the helper function uses
[getPlatformProxy](https://developers.cloudflare.com/workers/wrangler/api/#getplatformproxy)
method from wrangler. This logic is placed under a check if
import.meta.env.DEV is true.
The helper function uses [getPlatformProxy](/workers/wrangler/api/#getplatformproxy) method from wrangler to provide access to your Cloudflare bindings during local development. The bindings are cached at startup for better performance. In production, bindings are accessed via `process.env`. Make sure you've run `npm run cf-typegen` to generate the `Env` types that this code references.
</Details>

2. **Example using a Cloudflare Binding**
3. **Example using a Cloudflare Binding in Server Functions**

Now that you have a helper function to get access to your Cloudflare bindings, you can use them in your application.
Now that you have a helper function to get access to your Cloudflare bindings, you can use them in your server functions.

Remember bindings are only available on the server.

```ts
const bindings = await getBindings();
const cache = bindings.CACHE;
const queryCount = (await cache.get("queryCount")) || "0";
await cache.put("queryCount", String(Number(queryCount) + 1));
import { createServerFn } from "@tanstack/react-start";
import { getBindings } from "~/utils/bindings";

const personServerFn = createServerFn({ method: "GET" })
.validator((d: string) => d)
.handler(async ({ data: name }) => {
const env = getBindings();
let growingAge = Number((await env.CACHE.get("age")) || 0);
growingAge++;
await env.CACHE.put("age", growingAge.toString());
return { name, randomNumber: growingAge };
});
```

A special thanks to GitHub user [backpine](https://github.com/backpine) for the code that supports Cloudflare Bindings in TanStack, which is demonstrated in their [TanStack Start on Workers example](https://github.com/backpine/tanstack-start-on-cloudflare-workers-v0).
A special thanks to GitHub user [backpine](https://github.com/backpine) for the code that supports Cloudflare Bindings in TanStack Start, which is demonstrated in their [TanStack Start Beta on Cloudflare example](https://github.com/backpine/tanstack-start-beta-on-cloudflare).

</Steps>

#### Optional: Update utils file with deployment URL
## Environment Handling

The TanStack Start Beta version provides seamless environment handling:

This step is required for the `/users` page to function properly in the `start-basic` example. Update the `/src/utils/users.tsx` file with the Cloudflare Workers deployment URL.
- **Development**: Bindings are accessed via `getPlatformProxy()` from Wrangler and cached at startup
- **Production**: Bindings are accessed via `process.env`

```ts
export const DEPLOY_URL = "YOUR_DEPLOYMENT_URL";
```
This approach ensures your bindings are properly typed throughout your project and provides a smooth development experience.

By following the steps above, you will have deployed your TanStack Start application to Cloudflare Workers.
Loading