
How to deploy an Astro site to Vercel?
To deploy an Astro site to Vercel, push your project to GitHub, GitLab or Bitbucket, import the repository at vercel.com/new, and accept the settings Vercel detects for Astro. A static Astro site needs no configuration at all. If any page is rendered on demand, first run npx astro add vercel to install the @astrojs/vercel adapter, then deploy the same way. Every push to your production branch then creates a production deployment, and every push to another branch creates a preview deployment with its own URL. You can also deploy from your terminal with the Vercel CLI by running vercel in the project folder.
Vercel's Astro support is mature, but a few details trip people up: when the adapter is needed, how environment variables reach your code, which Node.js version runs your functions, and how features like ISR interact with middleware. In this article you'll learn both deployment methods, how to set up the adapter, what the build produces, how to handle environment variables and Node.js versions, how to cache on-demand pages with ISR, and how to fix the most common problems.
Static Site or Adapter?
Start by deciding which kind of project you're deploying.
- Fully static site: every page is prerendered at build time, which is Astro's default. Vercel runs
astro buildand serves thedistfolder from its CDN. No adapter and no config are needed. - Site with on-demand routes: if any page or endpoint uses
export const prerender = false, or your config setsoutput: 'server', you need the adapter so those routes can run as Vercel Functions. You also need it for Astro Actions, server islands and sessions. - Static site using Vercel features: the adapter is also required if you want Vercel Image Optimization or Web Analytics on a static site.
If you're unsure which pages are static, the build log lists every prerendered route. The rules for mixing the two kinds of page are covered in mixing prerendered and on-demand pages.
Adding the Vercel Adapter
Run astro add from your project root:
npx astro add vercel
It installs @astrojs/vercel and updates your config:
// astro.config.mjs
import { defineConfig } from "astro/config";
import vercel from "@astrojs/vercel";
export default defineConfig({
adapter: vercel(),
});
Import the adapter from the package root, @astrojs/vercel. Older guides, including some still on Vercel's own documentation at the time of writing, import from @astrojs/vercel/serverless or @astrojs/vercel/static and set output: 'hybrid'. Those subpaths are no longer exported by the current adapter, and the hybrid output mode no longer exists in Astro, so copy-pasting those examples will fail.
What the Adapter Builds
Run a build locally to see what Vercel will deploy:
npx astro build
The adapter writes Vercel's Build Output API format into .vercel/output:
.vercel/output/config.json
.vercel/output/static/index.html
.vercel/output/static/about/index.html
.vercel/output/functions/_render.func/.vc-config.json
Prerendered pages and assets go into static/, and every on-demand route is bundled into one function, _render. The routing rules in config.json serve existing files first and send the dynamic routes, such as /dashboard and /api/time in this test project, to the function. Files in /_astro/ get a one-year immutable Cache-Control header, because their names contain a content hash.
Add .vercel to your .gitignore. It is build output, and Vercel creates it again on every deployment.
Method 1: Deploy From Git
This is the method most teams use, because it gives you automatic deployments and previews.
- Push your project to a repository on GitHub, GitLab or Bitbucket.
- Import the repository at vercel.com/new and select it.
- Check the detected settings: Vercel recognises Astro and fills in the framework preset, build command and output settings. You normally don't need to change anything.
- Add environment variables if your build needs them (see below).
- Click Deploy. When it finishes you get a production URL on
vercel.app.
From then on, pushes to the production branch, usually main, create production deployments, and pushes to any other branch or pull request create preview deployments with unique URLs. Custom domains are added under the project's Domains settings.
Method 2: Deploy With the Vercel CLI
The CLI is handy for a first deployment or for projects that aren't in Git.
npm install -g vercel
vercel
The first run asks you to log in and link the folder to a new or existing project. Vercel detects Astro; when asked whether you want to override the settings, answer N. The command uploads your source, builds it on Vercel and prints a preview URL. To create a production deployment instead:
vercel --prod
Building Locally and Uploading the Output
If you want to build on your own machine or CI, so that Vercel never receives your source code, use vercel build followed by vercel deploy --prebuilt:
vercel pull --yes
vercel build --prod
vercel deploy --prebuilt --prod
vercel pull downloads your project settings and environment variables, vercel build runs astro build with the production variables and writes .vercel/output, and vercel deploy --prebuilt uploads that folder as-is. Because the Astro adapter already writes the Build Output API format, the Astro docs also show the shorter astro build followed by vercel deploy --prebuilt, but running vercel build makes sure the build sees the same environment variables as a normal Vercel build.
Environment Variables
Add variables under Settings > Environment Variables in your Vercel project. Each one can target Production, Preview, Development or a combination.
How your code reads them depends on when it runs:
import.meta.envvalues are replaced in the code at build time. In Astro 6 and later they are always inlined and never coerced, so"true"stays a string. Changing such a variable in Vercel requires a new deployment.process.envin on-demand routes reads the value when the function runs.astro:envgives you a typed schema, with secrets read at runtime through the adapter. It's the most portable option, explained in type-safe environment variables with astro:env.
Remember that only variables prefixed with PUBLIC_ are available in client-side code through import.meta.env. To work locally with the same values as Vercel, download them into a local file:
vercel env pull .env.local
Keep .env.local out of Git. For a full overview of the options, see how to set environment variables in Astro.
Setting the Node.js Version
Astro 7.4.2 declares "node": ">=22.23.4" in its engines field, so your build must run on a recent Node.js 22 or later. Vercel currently offers Node.js 24.x (the default for new projects) and 22.x. Choose the version under Settings > Build and Deployment > Node.js Version, or pin the major version in package.json, which overrides the dashboard setting:
{
"engines": {
"node": "24.x"
}
}
The adapter also checks your local version during the build. Building on a version that Vercel Functions don't offer gives a warning like this, and the function config falls back to a supported runtime:
[WARN] [@astrojs/vercel]
The local Node.js version (26) is not supported by Vercel Serverless Functions.
Your project will use Node.js 24 as the runtime instead.
Consider switching your local version to 24.
Caching On-Demand Pages With ISR
Vercel's Incremental Static Regeneration caches on-demand pages after the first request, so most visitors get a cached response while the page is still generated by a function. Enable it in the adapter:
// astro.config.mjs
import { defineConfig } from "astro/config";
import vercel from "@astrojs/vercel";
export default defineConfig({
adapter: vercel({
isr: {
expiration: 60,
exclude: ["/dashboard", /^\/api\/.+/],
},
maxDuration: 30,
}),
});
expiration: how long, in seconds, a cached page stays fresh. Without it, pages are cached for the lifetime of the deployment.exclude: routes that must always render fresh, such as personalised pages and APIs. Strings and regular expressions both work.maxDuration: the maximum time, in seconds, a function may run before timing out. Your plan sets the upper limit.
With this config, the build creates a second function, _isr, next to _render. Excluded routes go to _render and are rendered on every request.
Two caveats from the adapter docs are worth knowing. ISR requests don't include query string parameters, so don't cache pages that depend on ?search=. And by default your Astro middleware is bundled into the ISR function, so it only runs on a cache miss or regeneration, not on every request. If middleware must run every time, set middlewareMode: 'edge', which deploys it as an edge function in front of the cache. You can also invalidate pages on demand with a bypassToken. ISR is one of several approaches described in caching on-demand pages.
You don't have to use ISR to cache. On-demand pages can also set standard headers, which Vercel's CDN respects:
---
// src/pages/prices.astro
export const prerender = false;
Astro.response.headers.set('Cache-Control', 's-maxage=60, stale-while-revalidate=300');
---
<h1>Prices</h1>
Other Useful Adapter Options
imageService: true: uses Vercel Image Optimization for Astro's<Image />and<Picture />components in production.webAnalytics: { enabled: true }: injects Vercel Web Analytics with@vercel/analytics1.3 or earlier. With 1.4 and later, use Vercel's own Analytics component instead.skewProtection: true: keeps users on the same deployment version during a rollout. It requires a Pro or Enterprise plan.includeFiles/excludeFiles: force files into, or out of, the function bundle, for example a JSON file you read withfs.
Sessions Need a Driver
The Node, Netlify and Cloudflare adapters configure session storage automatically. Vercel doesn't, so if you use Astro.session you must configure a driver, such as Redis from the Vercel Marketplace. If you don't use sessions, set session: false in your Astro config to leave the session runtime out of your function.
Troubleshooting
- A page that should be dynamic shows stale content: check that it has
export const prerender = false, or that it isn't being cached by ISR. Add the route toisr.excludeif it must always be fresh. - "Cannot use server-rendered pages without an adapter": you have an on-demand route but no adapter. Run
npx astro add vercel. - Build fails with a Node.js engine error: set the project's Node.js version to 22.x or 24.x, or add an
enginesfield. - Files read at runtime are missing: list them in the adapter's
includeFilesoption so they're bundled with the function. - Rewrites in
vercel.jsonbehave oddly: Vercel's Astro guide says rewrites invercel.jsononly work for static files with Astro. Use Astro's ownredirectsconfig,Astro.redirect()or middleware for dynamic routes. - Middleware isn't running on cached pages: with ISR it only runs on cache misses unless you set
middlewareMode: 'edge'.
FAQ: Deploying Astro to Vercel
No. Vercel deploys a fully static Astro site with zero configuration. You only need @astrojs/vercel for on-demand pages, actions, server islands or sessions, or to use Vercel Image Optimization or Web Analytics.
Vercel has a free Hobby plan that works for personal Astro sites, with limits on usage and features. Commercial projects and features such as skew protection need a paid plan. Check Vercel's pricing page for current limits.
Values read through import.meta.env are inlined into the code at build time, so you need to redeploy after changing them. Variables read at runtime through process.env or astro:env secrets come from the function environment, but Vercel still applies environment changes to new deployments, so redeploy either way.
Yes. Set middlewareMode: 'edge' in the adapter options and the adapter creates an edge function from your Astro middleware. It runs for every request to an on-demand route, including routes cached by ISR, but not for static assets or prerendered pages.
New Vercel projects default to Node.js 24.x, and 22.x is also available. Astro 7.4.2 requires Node.js 22.23.4 or later, so either works. Set it in the project settings or with the engines field in package.json.
With the Git integration, push the branch or open a pull request and Vercel creates a preview deployment automatically. With the CLI, running vercel without --prod creates a preview deployment.
Conclusion
Deploying Astro to Vercel is mostly automatic. Static sites deploy with no configuration from a Git import or a single vercel command, and sites with on-demand routes need only the @astrojs/vercel adapter, imported from the package root. The adapter turns your dynamic routes into a Vercel Function and writes everything else as static files on Vercel's CDN.
Once it's running, spend your attention on the details that differ between projects: set environment variables for the right environments and remember that import.meta.env is fixed at build time, pick a Node.js version that satisfies Astro's requirement, and use ISR or cache headers for on-demand pages that don't need to be rendered on every request. Keep middleware and ISR in mind together, and most production surprises won't happen.


