# Migrate an Astro app from Webflow Cloud to Vercel

**Author:** Ben Sabic

---

Moving an Astro app from Webflow Cloud to Vercel mostly means swapping the adapter that targets the Cloudflare Workers runtime for one that targets Vercel's. On Webflow Cloud, your app runs as a Cloudflare Worker through the `@astrojs/cloudflare` adapter. On Vercel, the same app runs on [Vercel Functions](https://vercel.com/docs/functions) with [Fluid compute](https://vercel.com/fluid) enabled by default, in a full Node.js runtime, so it scales automatically and supports Astro's full on-demand rendering feature set.

This guide walks you through the full migration. You'll replace the `@astrojs/cloudflare` adapter with `@astrojs/vercel`, remove Wrangler, delete the Webflow Cloud configuration files, and drop the base path your app used to mount under a Webflow site. It also covers replacing Cloudflare storage bindings with their Vercel equivalents (for example, Object Storage with [Vercel Blob](https://vercel.com/storage/blob)), recreating environment variables, and deploying with Git or the `vercel` CLI.

## Prerequisites

Before you begin, make sure you have:

- An Astro app deployed on Webflow Cloud
  
- A [Vercel account](https://vercel.com/signup)
  
- Vercel CLI installed (`npm i -g vercel`)
  
- Node.js 20 or later
  

## Migrate with an agent

If you use an AI coding agent like Claude Code or Cursor, you can have it handle most of the migration for you and provide expert guidance. Install the [Vercel Plugin](https://vercel.com/docs/agent-resources/vercel-plugin) to provide your agent with Vercel-specific context, then add the companion skill for this guide.

**Install the Vercel Plugin:**

`npx plugins add vercel/vercel-plugin`

**Add the Astro migration skill:**

`npx skills add vercel-labs/vercel-kb-skills --skill astro-webflow-to-vercel`

With both in place, ask your agent to migrate your Astro app from Webflow Cloud to Vercel. Your agent will follow the migration steps and apply Vercel's recommended patterns for the Vercel adapter, storage solutions, environment variables, and more.

## What changes when you move your project

On Webflow Cloud, your Astro app runs as a Cloudflare Worker. The `@astrojs/cloudflare` adapter compiles it for the `workerd` runtime, Webflow Cloud serves it from a mount path inside an environment (e.g., `/app`), and your server code reads storage through bindings on the Cloudflare runtime exposed via Astro's `locals` object.

On Vercel, the same application runs on Vercel Functions in a full Node.js runtime. Vercel auto-detects Astro on import, serves your app from the root, reads configuration from `process.env`, and connects to [storage providers in the Vercel Marketplace](https://vercel.com/docs/marketplace-storage) through native integrations.

The table below maps each Webflow Cloud component to its Vercel counterpart.

| Webflow Cloud                                         | Vercel                                                                                                                                                           |
| ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Cloudflare Workers runtime (`workerd`)                | Vercel Functions (Fluid compute)                                                                                                                                 |
| `@astrojs/cloudflare` adapter                         | `@astrojs/vercel` adapter                                                                                                                                        |
| `platformProxy` (adapter option for local dev)        | Not needed                                                                                                                                                       |
| `react-dom/server.edge` Vite alias                    | Not needed (full Node.js runtime)                                                                                                                                |
| `webflow.json`                                        | Not needed, Astro is auto-detected                                                                                                                               |
| `wrangler.json`                                       | `vercel.json` (optional)                                                                                                                                         |
| `worker-configuration.d.ts`                           | Not needed                                                                                                                                                       |
| `base` and `build.assetsPrefix` (mount path)          | Served from the root; remove unless you want a base path                                                                                                         |
| `locals.runtime.env` and `Astro.locals.runtime.env`   | `process.env`                                                                                                                                                    |
| `dev.vars` (local runtime variables)                  | `.env` via `vercel env pull`                                                                                                                                     |
| `webflow cloud deploy` or GitHub push                 | Git push or the `vercel` CLI                                                                                                                                     |
| `npm` only                                            | npm, pnpm, Yarn, or Bun                                                                                                                                          |
| Object Storage (R2 binding)                           | [Vercel Blob](https://vercel.com/storage/blob)                                                                                                                   |
| Key Value Store (Workers KV binding)                  | [Redis from the Vercel Marketplace](https://vercel.com/marketplace?search=Redis), or [Edge Config](https://vercel.com/storage/edge-config) for read-heavy config |
| SQLite (D1 binding)                                   | [Postgres from the Vercel Marketplace](https://vercel.com/marketplace?search=postgres)                                                                           |
| `export const config = { runtime: 'edge' }` on routes | Remove it to run on Node.js (recommended)                                                                                                                        |
| Edge runtime API routes                               | Astro Server Endpoints on Vercel Functions                                                                                                                       |
| Astro middleware on the Edge runtime                  | Astro middleware on Vercel Functions, or at the Edge with `middlewareMode: 'edge'`                                                                               |
| No scheduled jobs, queues, or workflows               | [Cron Jobs](https://vercel.com/docs/cron-jobs), [Queues](https://vercel.com/docs/queues), and [Workflows](https://vercel.com/workflows)                          |

## Steps

### 1\. Replace the Cloudflare adapter with the Vercel adapter

Astro needs an adapter to server-render on each platform, so the core setup change is swapping one adapter for the other.

Uninstall the Cloudflare adapter and Wrangler, then install the Vercel adapter:

`npm uninstall @astrojs/cloudflare wrangler npm install @astrojs/vercel`

Update `astro.config.mjs` to import and use the Vercel adapter. Keep `output: 'server'` to render every route on demand as a Vercel function, which matches how your app ran on Webflow Cloud:

`import { defineConfig } from "astro/config"; import vercel from "@astrojs/vercel"; import react from "@astrojs/react"; export default defineConfig({ output: "server", adapter: vercel(), integrations: [react()], });` If most of your pages are static and only a few are dynamic, set `output: "static"` instead and add `export const prerender = false` to the components that need server rendering. Two Cloudflare-specific options can go at the same time. Remove the `platformProxy` setting, which only configured the Workers runtime for local development, and remove the `react-dom/server.edge` Vite alias, a workaround for the Workers environment that Vercel's Node.js runtime doesn't need. ### 2\. Remove the Webflow Cloud configuration files Delete the Cloudflare- and Webflow-specific files that no longer apply on Vercel: - `webflow.json`, which told Webflow Cloud your framework. Vercel detects Astro automatically.    - `wrangler.json`, including its `compatibility_date`, `nodejs_compat` flag, `assets` binding, and storage bindings. Vercel Functions run on Node.js, so the compatibility flags have no equivalent.    - `worker-configuration.d.ts`, the generated binding types.    Your storage bindings live in `wrangler.json`. Before deleting the file, note which bindings your app uses (Object Storage, Key Value Store, or SQLite) so you can recreate them on Vercel in step five. ### 3\. Remove the base path and Edge runtime settings On Webflow Cloud, your app is served from a mount path such as `/app`, so you set `base` and `build.assetsPrefix` in `astro.config.mjs` to match. On Vercel, your app is served from the root, so remove both options unless you intend to keep serving the app under a subpath: `import { defineConfig } from "astro/config"; import vercel from "@astrojs/vercel"; export default defineConfig({ // Remove base and build.assetsPrefix when serving from the root // base: "/app", // build: { assetsPrefix: "/app" }, output: "server", adapter: vercel(), });` Because the base path is gone, remove the manual prefixing Webflow Cloud required in client-side `fetch` calls and asset references. Change `fetch(${import.meta.env.BASE_URL}/api/users)` back to `fetch("/api/users")`, and drop `import.meta.env.ASSETS_PREFIX` from plain `<img>` tags and from the favicon link in your layout. Webflow Cloud also runs API routes on the Edge runtime. On Vercel, remove the `export const config = { runtime: 'edge' }` directive from your Server Endpoints so they run on the default Node.js runtime. We recommend migrating from the Edge runtime to Node.js for improved performance and reliability, and both runtimes run on [Fluid compute](https://vercel.com/fluid) with [Active CPU pricing](https://vercel.com/docs/functions/usage-and-pricing#active-cpu). Running on Node.js also gives your routes access to the full Node.js API surface and to npm packages that depend on Node.js built-ins.

### 4\. Update your build scripts

Replace the Webflow Cloud preview script in `package.json` with the standard Astro commands. Vercel runs the build for you, so you no longer need Wrangler to preview the Workers build:

`{ "scripts": { "dev": "astro dev", "build": "astro build", "preview": "astro preview" } }`

Vercel auto-detects Astro on import and sets the build command and output directory, so these scripts mainly support local development.

### 5\. Replace Webflow Cloud bindings with Vercel storage

This is the core code change.

On Webflow Cloud, you read storage through bindings on the Cloudflare runtime, which you access in a Server Endpoint through the `locals` object.

On Vercel, you read connection details from `process.env` and talk to each store through its SDK or client. Remove every `locals.runtime.env` and `Astro.locals.runtime.env` access, and replace the binding operations.

For example, an Object Storage upload on Webflow Cloud looks like this:

`// Before (Webflow Cloud Object Storage, R2 binding) export const config = { runtime: "edge" }; import type { APIRoute } from "astro"; export const POST: APIRoute = async ({ request, locals }) => { const { key, content } = await request.json(); const { env } = (locals as any).runtime; await env.MEDIA_BUCKET.put(key, content); return new Response(JSON.stringify({ success: true })); };`

On Vercel, the same upload uses Vercel Blob:

`// After (Vercel Blob) import type { APIRoute } from "astro"; import { put } from "@vercel/blob"; export const POST: APIRoute = async ({ request }) => { const { key, content } = await request.json(); const blob = await put(key, content, { access: "public" }); return new Response(JSON.stringify({ url: blob.url })); };`

Install the Blob SDK with `npm i @vercel/blob`, then create a Blob store from the [Storage page](https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fstores) in your Vercel dashboard and connect it to your project. Vercel adds the store's environment variables, and the SDK uses them automatically, so the `put()` call above needs no token in your code. This [OIDC approach](https://vercel.com/docs/vercel-blob/using-blob-sdk#oidc-tokens-recommended) is recommended over the long-lived `BLOB_READ_WRITE_TOKEN`, which you'd use only for code that runs outside Vercel.

Map your other Webflow Cloud storage the same way:

- Key Value Store becomes a Redis integration (e.g., [Upstash Redis](https://vercel.com/marketplace/upstash)) from the Vercel Marketplace for caching and session data, or Edge Config for small, read-heavy configuration.
  
- SQLite becomes a Postgres database (e.g., [Neon](https://vercel.com/marketplace/neon)) from the Vercel Marketplace. If you used Drizzle ORM with SQLite on Webflow Cloud, Drizzle supports Postgres too, so you keep your schema-first workflow.
  

When you provision storage from the Marketplace, Vercel adds the connection string and credentials as environment variables, which your code reads from `process.env`.

### 6\. Move your environment variables and secrets

Recreate your Webflow Cloud environment variables as Vercel environment variables. Webflow Cloud stores these per environment in your app's settings and injects them at runtime only. Vercel stores them per environment (production, preview, and development) in project settings and makes them available at both build time and runtime.

Add each variable to your [project's environment variables](https://vercel.com/d?to=%2F%5Bteam%5D%2F%5Bproject%5D%2Fsettings%2Fenvironment-variables), or from the CLI:

`vercel env add DATABASE_URL production`

To run your app locally with the same values, link the project and pull the variables into a local `.env` file. You can delete the `dev.vars` file Webflow Cloud used for local runtime variables, since Astro loads `.env` automatically:

`vercel link vercel env pull`

The way you read variables changes, too.

On Webflow Cloud, custom server variables came from `Astro.locals.runtime.env` in components and `locals.runtime.env` in Server Endpoints.

On Vercel's Node.js runtime, read them from `process.env` (for example, `process.env.DATABASE_URL`). For variables you want exposed to client-side code, use Astro's `PUBLIC_` prefix and `import.meta.env`.

Because Vercel exposes environment variables during the build, you can re-enable any build-time validation you had to disable on Webflow Cloud, where variables aren't available at build time.

### 7\. Deploy to Vercel

You have two deployment options. Both run your app on Vercel Functions.

**Deploy with Git (recommended):**

1. Push your project to GitHub, GitLab, or Bitbucket.
   
2. In the [Vercel dashboard](https://vercel.com/), select **Add New** > **Project**, then import your repository.
   
3. Vercel detects Astro and sets the build command and output directory. Confirm the framework preset, add your environment variables, and select Deploy.
   

If the framework preset or build settings need correcting after import, you can update them from the Vercel CLI (requires v54.21.1+):

`vercel project update <project-name> --framework astro --build-command "<cmd>" --output-directory <dir>`

After the first import, every push to your main branch creates a production deployment, and every pull request gets its own preview URL.

**Deploy with the CLI:**

`vercel`

Run `vercel` from your project root to create a preview deployment, or `vercel --prod` to deploy to production.

Once deployed, your app runs on Vercel Functions with Fluid compute, with preview deployments, observability, and the Vercel Firewall available.

## Troubleshooting

### "Cannot find module '@astrojs/cloudflare'"

Your config or server code still references the Cloudflare adapter. Search your project for `@astrojs/cloudflare` and `locals.runtime`, then remove the adapter import and `platformProxy` from `astro.config.mjs` and replace each binding call with its Vercel equivalent from step five. The adapter module is only resolved within the build that targets Cloudflare Workers.

### Reading `runtime` throws "Cannot read properties of undefined"

Code still reads `locals.runtime.env`, which only exists when the Cloudflare adapter populates it. On Vercel, that object isn't present. Replace `locals.runtime.env.VARIABLE` with `process.env.VARIABLE` for secrets and connection strings, and use the relevant SDK for storage, as shown in step five.

### Assets or routes return a 404

A leftover base path is usually the cause. Confirm you removed `base` and `build.assetsPrefix` from `astro.config.mjs`, and remove any manual base-path prefixing from client-side `fetch` calls, `<img>` tags, and your layout's favicon link. On Vercel, your app is served from the root, so paths like `/api/users` and `/logo.png` resolve without a prefix.

### Environment variables are undefined at runtime

Confirm each variable exists in the correct environment under your [project's environment variables](https://vercel.com/d?to=%2F%5Bteam%5D%2F%5Bproject%5D%2Fsettings%2Fenvironment-variables), then redeploy. Variables added to production aren't available in preview or development unless you add them there too. For local runs, re-run `vercel env pull` after changing variables.

## Best practices

- Tune function resources. If your routes need more time than the default, set `maxDuration` in your Vercel adapter configuration (for example, `adapter: vercel({ maxDuration: 60 })`), or configure memory and regions in `vercel.json`.
  
- Place functions near your data. Set the function region close to your database region to reduce latency.
  
- Take advantage of features that were limited on Webflow Cloud. Incremental Static Regeneration (`adapter: vercel({ isr: true })`), Image Optimization with Astro's `Image` component, Web Analytics, and Speed Insights all work on Vercel with minimal configuration.
  

## Related resources

- [Astro on Vercel](https://vercel.com/docs/frameworks/frontend/astro)
  
- [Vercel Functions](https://vercel.com/docs/functions) and [Fluid compute](https://vercel.com/docs/fluid-compute)
  
- [The](https://docs.astro.build/en/guides/integrations-guide/vercel/) [@astrojs/vercel](https://docs.astro.build/en/guides/integrations-guide/vercel/) [adapter](https://docs.astro.build/en/guides/integrations-guide/vercel/)
  
- [Incremental Static Regeneration on Vercel](https://vercel.com/docs/incremental-static-regeneration)
  
- [Storage on the Vercel Marketplace](https://vercel.com/docs/marketplace-storage) and [Vercel Blob](https://vercel.com/docs/vercel-blob)
  
- [Managing environment variables](https://vercel.com/docs/environment-variables)
  
- [Image Optimization](https://vercel.com/docs/image-optimization)

---

[View full KB sitemap](/kb/sitemap.md)
