Skip to main content
The @sveltejs/adapter-vercel adapter deploys your SvelteKit app to Vercel.

Installation

npm install -D @sveltejs/adapter-vercel

Usage

Add the adapter to your svelte.config.js:
/// file: svelte.config.js
import adapter from '@sveltejs/adapter-vercel';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    adapter: adapter({
      runtime: 'nodejs24.x',
      regions: ['iad1'],
      split: false
    })
  }
};

export default config;

Configuration options

From the adapter type definitions:
/// file: packages/adapter-vercel/index.d.ts
export interface ServerlessConfig {
  runtime?: Exclude<RuntimeConfigKey, 'edge'>;
  regions?: string[];
  maxDuration?: number;
  memory?: number;
  split?: boolean;
  isr?: {
    expiration: number | string | false;
    bypassToken?: string;
    allowQuery?: string[] | undefined;
  } | false;
}

export type Config = (EdgeConfig | ServerlessConfig) & {
  images?: ImagesConfig;
};

runtime

The runtime to use for your app. Can be a Node.js version or 'edge'.
adapter({
  runtime: 'nodejs24.x'
})
The runtime: 'edge' option is deprecated and will be removed in a future version. Use serverless functions instead.

regions

An array of Vercel Edge Network regions where your app will be deployed.
adapter({
  regions: ['iad1', 'sfo1']  // US East and West
})

maxDuration

Maximum execution duration in seconds for serverless functions. Available on Pro and Enterprise plans.
adapter({
  maxDuration: 60  // 60 seconds
})

memory

Amount of memory (in MB) allocated to serverless functions.
adapter({
  memory: 3008  // 3GB
})

split

If true, each route will be deployed as a separate function. Useful for large apps to stay within function size limits.
adapter({
  split: true
})
From the implementation:
/// file: packages/adapter-vercel/index.js
const id = config.split ? `${hash}-${groups.size}` : hash;
let group = groups.get(id);
if (!group) {
  group = { i: groups.size, config, routes: [] };
  groups.set(id, group);
}

isr

Incremental Static Regeneration configuration. Only works with serverless functions.
adapter({
  isr: {
    expiration: 60  // Revalidate every 60 seconds
  }
})
You can also configure ISR per-route:
/// file: src/routes/blog/[slug]/+page.server.js
/** @type {import('./$types').PageServerLoad} */
export const load = async ({ params }) => {
  // ...
};

export const config = {
  isr: {
    expiration: 60
  }
};
ISR requires a serverless runtime. It does not work with edge functions.

images

Configuration for Vercel Image Optimization:
adapter({
  images: {
    sizes: [640, 828, 1200, 1920, 3840],
    formats: ['image/avif', 'image/webp'],
    domains: ['example.com'],
    minimumCacheTTL: 300
  }
})

Per-route configuration

You can export a config object from +page.server.js or +server.js files to configure individual routes:
/// file: src/routes/api/heavy/+server.js
export const config = {
  runtime: 'nodejs24.x',
  maxDuration: 60,
  memory: 3008,
  regions: ['sfo1']
};

/** @type {import('./$types').RequestHandler} */
export async function GET() {
  // Heavy computation
  return new Response('Done');
}
From the adapter implementation:
/// file: packages/adapter-vercel/index.js
for (const route of builder.routes) {
  const runtime = resolve_runtime(defaults.runtime, route.config.runtime);
  const config = { ...defaults, ...route.config, runtime };
  // ...
}

Build output

The adapter generates a .vercel/output directory:
.vercel/output/
├── config.json              # Routing configuration
├── static/                  # Static assets
│   └── _app/
│       ├── immutable/
│       └── version.json
└── functions/
    ├── ![-]/
    │   └── catchall.func/   # Main serverless function
    │       ├── index.js
    │       └── .vc-config.json
    └── index.func -> ![-]/catchall.func  # Symlink for observability
The configuration defines how requests are routed:
/// file: packages/adapter-vercel/index.js
const static_config = {
  version: 3,
  routes: [
    {
      src: '.*',
      continue: true,
      transforms: [
        {
          type: 'request.query',
          op: 'delete',
          target: { key: '__pathname' }
        }
      ]
    },
    {
      src: `/${builder.getAppPath()}/immutable/.+`,
      headers: {
        'cache-control': 'public, immutable, max-age=31536000'
      }
    },
    { handle: 'filesystem' },
    { src: '/.*', dest: '/![-]/catchall' }
  ],
  overrides,
  images
};

Platform object

Vercel provides platform-specific context via event.platform:
interface Platform {
  // Available with Edge runtime
  context?: RequestContext;
}

interface RequestContext {
  waitUntil(promise: Promise<unknown>): void;
}
Example usage:
/// file: src/hooks.server.js
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
  const response = await resolve(event);
  
  // Log analytics in the background
  event.platform?.context?.waitUntil(
    fetch('https://analytics.example.com', {
      method: 'POST',
      body: JSON.stringify({
        path: event.url.pathname,
        status: response.status
      })
    })
  );
  
  return response;
}

Environment variables

Vercel provides several automatic environment variables:
VariableDescription
VERCELAlways '1' on Vercel
VERCEL_ENV'production', 'preview', or 'development'
VERCEL_URLDeployment URL
VERCEL_DEPLOYMENT_IDUnique deployment identifier
Access them in your app:
import { env } from '$env/dynamic/private';

export function load() {
  if (env.VERCEL_ENV === 'production') {
    // Production-only logic
  }
}

Deployment

1

Build your app

npm run build
2

Deploy to Vercel

vercel --prod

Skew protection

Vercel automatically enables skew protection to prevent version mismatches:
/// file: packages/adapter-vercel/index.js
if (process.env.VERCEL_SKEW_PROTECTION_ENABLED) {
  routes.push({
    src: '/.*',
    has: [
      {
        type: 'header',
        key: 'Sec-Fetch-Dest',
        value: 'document'
      }
    ],
    headers: {
      'Set-Cookie': `__vdpl=${process.env.VERCEL_DEPLOYMENT_ID}; Path=${builder.config.kit.paths.base}/; SameSite=Strict; Secure; HttpOnly`
    },
    continue: true
  });
}

Cron jobs

Vercel supports scheduled functions via vercel.json:
{
  "crons": [
    {
      "path": "/api/cron/daily",
      "schedule": "0 0 * * *"
    }
  ]
}
The adapter validates cron paths against your routes:
/// file: packages/adapter-vercel/index.js
function validate_vercel_json(builder, vercel_config) {
  const valid_routes = builder.routes.filter((route) => 
    route.api.methods.includes('GET')
  );
  
  for (const cron of crons) {
    if (!valid_routes.some((route) => route.pattern.test(cron.path))) {
      unmatched_paths.push(cron.path);
    }
  }
}
Vercel automatically detects VERCEL=true during builds, which is how adapter-auto identifies the Vercel environment.