The most surprising thing about deploying SvelteKit to Vercel is how much of the "deployment" is actually just SvelteKit configuring itself for Vercel’s environment.
Let’s see it in action. Imagine you have a simple SvelteKit app. You’ve written your routes, maybe a little server-side logic:
// src/routes/api/hello/+server.js
export function GET() {
return new Response(JSON.stringify({ message: "Hello from SvelteKit on Vercel!" }), {
headers: { 'Content-Type': 'application/json' }
});
}
Your svelte.config.js is the key. To deploy to Vercel, you need the @sveltejs/adapter-vercel adapter.
// svelte.config.js
import adapter from '@sveltejs/adapter-vercel';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter()
}
};
export default config;
That’s it for the SvelteKit side. You run npm run build. SvelteKit, with the adapter-vercel in place, doesn’t just bundle your code; it generates Vercel-specific deployment artifacts. It creates a .vercel directory containing configuration files that Vercel understands, and it structures your output for Vercel’s serverless functions and static hosting.
When you push this to a Git repository connected to Vercel, Vercel takes over. It detects the SvelteKit project and, because of the adapter-vercel, knows exactly how to build and deploy it. It sees the generated .vercel directory and uses that to configure its build process and deployment targets.
The adapter-vercel intelligently handles routing. For static assets (like your CSS, JS, and images), it tells Vercel to serve them directly from its edge network. For dynamic routes or API endpoints (like our /api/hello example), it configures them to run as Vercel Serverless Functions. This means your JavaScript code for those routes gets deployed as individual, on-demand functions.
Your package.json is also important, but mostly for Vercel’s build process. Vercel will typically run npm install (or yarn install, etc.) and then execute the build command specified in your package.json (usually npm run build). The adapter ensures that the output of this build is compatible with Vercel’s deployment environment.
The mental model here is that SvelteKit, via its adapter, is generating the deployment configuration for your target platform. You’re not manually creating serverless function definitions or static asset rules; SvelteKit does it for you based on the adapter you choose. The adapter-vercel translates SvelteKit’s build output into the specific format Vercel expects.
You might be wondering about environment variables. SvelteKit’s env.public and env.private are handled by Vercel’s built-in environment variable management. When you set an environment variable in your Vercel project settings (e.g., API_KEY), SvelteKit can access it during the build process (for public variables) or at runtime (for private variables) if you’ve configured them correctly in svelte.config.js and your .env files. For example, if you have a public variable PUBLIC_ANALYTICS_ID, you’d access it in your Svelte components or server routes as import { env } from '$env/dynamic/public'; and then env.PUBLIC_ANALYTICS_ID. Vercel injects these at build time or runtime as appropriate for your configuration.
The adapter-vercel also handles prerendering. If you’ve configured export const prerender = true; for a page in SvelteKit, the adapter will ensure that the HTML for that page is generated at build time and served as a static file by Vercel, giving you fast initial loads.
Understanding how the adapter translates your SvelteKit project into Vercel’s infrastructure is key. You’re not just deploying code; you’re deploying a configuration that tells Vercel how to run your app as efficiently as possible, leveraging serverless functions for dynamic parts and edge caching for static assets.
After deploying, the next thing you’ll likely encounter is optimizing your serverless function performance or managing cache invalidation for prerendered pages.