# How to ship an H3 app on Vercel

**Author:** Ben Sabic

---

[H3](https://h3.dev/) is a lightweight, fast, and composable server framework for modern JavaScript runtimes. It's built on web-standard primitives like `Request`, `Response`, `URL`, and `Headers`, and instead of shipping one large core, you start with a small app instance and add tree-shakable utilities for the features you need.

On Vercel, you can deploy an H3 app with zero configuration: your app runs as a single [Vercel Function](https://vercel.com/docs/functions) on [Fluid compute](https://vercel.com/docs/fluid-compute), and you get response streaming, preview deployments, and observability without extra setup.

This guide walks you through deploying an H3 app to Vercel from the official example, the [Vercel CLI](https://vercel.com/docs/cli), or a Git repository, then configuring features such as streaming, middleware, cron jobs, the Bun runtime, and observability.

## Prerequisites

Before you begin, make sure you have:

- A [Vercel account](https://vercel.com/signup)
  
- Node.js 20+ and a package manager (e.g., npm)
  
- An existing H3 project, or a new one created from the [H3 quick start](https://h3.dev/guide)
  
- A Git repository on GitHub, GitLab, or Bitbucket (if you want Git-based deployments)
  
- Vercel CLI installed (`npm i -g vercel`)
  

## How it works

When you deploy an H3 app, Vercel detects the framework and builds it for the Vercel runtime. Your H3 app handles requests through a single [Vercel Function](https://vercel.com/docs/functions), which runs on Fluid compute by default. Your app scales up and down with traffic, and you pay only for the compute it uses, not for idle time.

Because Vercel ships zero-configuration detection for H3, you don't set a build command or output directory. Vercel reads your project, identifies the file that exports your H3 app as the default export, and applies the appropriate build settings. H3 is built on web standards, so its app instance exposes a web `fetch` handler that Vercel runs for every request.

## Deploy your H3 app

You can ship an H3 app to Vercel in three ways. Choose the one that fits where your code lives today.

### Option 1: Deploy from the official example

The fastest way to ship an H3 app is to start from Vercel's maintained H3 example. Vercel clones the [example repository](https://github.com/vercel/vercel/tree/main/examples/h3) to your Git provider, creates a project, and deploys it with zero configuration.

[Deploy in a single click](https://vercel.com/new/clone?repository-url=https://github.com/vercel/vercel/tree/main/examples/h3).

### Option 2: Start a new project with the Vercel CLI

To scaffold a new H3 project locally, use the [Vercel CLI init command](https://vercel.com/docs/cli/init). It clones Vercel's H3 example into a folder named `h3`.

1. Create the project:
   
   `vercel init h3`
   
2. Install dependencies:
   
   `cd h3 npm install`
   
3. Develop locally at `http://localhost:3000`. Run it with the Vercel CLI, so your app behaves the same way it does in production:
   
   `vercel dev`
   
4. Create a [preview deployment](https://vercel.com/docs/deployments/environments#preview-environment-pre-production). The first run links the project:
   
   `vercel`
   
5. Promote your deployment to production:
   
   `vercel --prod`
   

### Option 3: Deploy an existing H3 app

If you already have an H3 app, deploy it from Git or from the command line.

From Git: Push your project to GitHub, GitLab, or Bitbucket, then import it at [vercel.com/new](https://vercel.com/new). Vercel detects H3 automatically and deploys it with zero configuration.

From the CLI: From your project's root directory, run `vercel` to create a preview deployment, then `vercel --prod` to go live. To pull project settings and environment variables for local development, run:

`vercel link vercel env pull`

For Vercel to detect your app, export your H3 instance as the default export from one of the recognized entry files, such as `app.ts`, `index.ts`, or `server.ts` at your project root or under `src/`:

`import { H3 } from 'h3'; const app = new H3(); app.get('/', (event) => { return { message: 'Hello from H3 on Vercel' }; }); export default app;`

H3's app instance is a web `fetch` handler, so you can also export it explicitly with `export const fetch = app.fetch` if your setup needs the named web-standard export.

## Use Vercel features with H3

After your app is deployed, you can layer Vercel features onto it. Some work automatically, and others take a few lines of configuration in `vercel.json`.

### Your app runs as a single Vercel Function

Vercel bundles your entire H3 app into a single [Vercel Function](https://vercel.com/docs/functions). Every incoming request goes to that function, and H3's router matches the path and method to your route handlers, middleware, and error handling.

This function uses Fluid compute by default, which runs multiple requests concurrently within a single instance to reduce cold starts and the cost of I/O-bound work such as API calls and database queries. You don't configure anything to get this behavior.

Because your entire app ships as a single bundle, it must fit within the 250 MB limit for Vercel Functions. Vercel removes unneeded files from the bundle to keep it small, but it doesn't bundle your application code with a tool like Webpack or Rollup.

### Stream responses

Vercel Functions stream responses by default on Node.js, so you can send data to the client as you produce it instead of waiting for the full response. H3 sends a stream whenever a handler returns a `ReadableStream`, and you set response headers ahead of time on `event.res`:

`import { H3 } from 'h3'; const app = new H3(); app.get('/stream', (event) => { event.res.headers.set('Content-Type', 'text/plain; charset=utf-8'); return new ReadableStream({ async start(controller) { const encoder = new TextEncoder(); for (const chunk of ['Hello', ' ', 'from', ' ', 'H3']) { controller.enqueue(encoder.encode(chunk)); await new Promise((resolve) => setTimeout(resolve, 200)); } controller.close(); }, }); }); export default app;` Streaming pairs well with Fluid compute: while your function waits between chunks, the same instance can serve other requests. To stream AI model output, [AI SDK](https://ai-sdk.dev/docs/foundations/streaming) handles the response formatting for you.

### Combine H3 middleware with Vercel Routing Middleware

H3 and Vercel each have a middleware layer, and they solve different problems. H3 middleware runs inside your app's router, after the request reaches your function. Use it for app-level concerns such as logging, CORS, and authentication. H3 also parses request bodies through its built-in utilities, so you don't add a separate body parser:

``import { H3, onRequest } from 'h3'; const app = new H3(); app.use( onRequest((event) => { console.log(`${event.req.method} ${event.url.pathname}`); }), ); app.post('/posts', async (event) => { const body = await event.req.json(); return { ok: true, received: body }; }); export default app;``

For CORS, authentication, cookies, and validation, use the corresponding helper from [H3's built-in utilities](https://h3.dev/utils) rather than installing separate middleware. [Vercel Routing Middleware](https://vercel.com/docs/routing-middleware) runs at the edge, before the request reaches your H3 app. Use it for rewrites, redirects, and header changes that should happen before any function runs. The two layers work together, with Routing Middleware shaping the request at the edge and H3 middleware handling it inside your app.

### Serve static assets from the CDN

To serve static files such as images, fonts, or a favicon, place them in the `public/**` directory. Vercel serves them through its [CDN](https://vercel.com/docs/cdn) using default [headers](https://vercel.com/docs/headers), which you can override in `vercel.json`. H3 is built for handling requests in code, so for static files rely on the `public` directory and keep your route handlers for dynamic responses.

### Run scheduled tasks with cron jobs

Vercel [Cron Jobs](https://vercel.com/docs/cron-jobs) trigger a route on a schedule by sending an HTTP GET request to it. Define a route in your H3 app for the task, then register the schedule in `vercel.json`.

**Define the route:**

``import { H3 } from 'h3'; const app = new H3(); app.get('/api/cron/cleanup', (event) => { if (event.req.headers.get('authorization') !== `Bearer ${process.env.CRON_SECRET}`) { event.res.status = 401; return 'Unauthorized'; } // Run your scheduled work here return { ok: true }; }); export default app;``

**Register the schedule:**

`{ "$schema": "https://openapi.vercel.sh/vercel.json", "crons": [{ "path": "/api/cron/cleanup", "schedule": "0 0 * * *" }] }` Vercel runs cron jobs only on production deployments. To stop anyone else from calling the route, set a `CRON_SECRET` environment variable in your project settings. Vercel sends it as a `Bearer` token in the `Authorization` header on every cron invocation, and your handler compares it before running the task. ### Use the Bun runtime H3 runs your function on Node.js by default. To run it on [Bun](https://vercel.com/docs/functions/runtimes/bun) instead, set `bunVersion` in `vercel.json`:

`{ "$schema": "https://openapi.vercel.sh/vercel.json", "bunVersion": "1.x" }`

Vercel detects the setting, runs your app on Bun in both `vercel dev` and production, and keeps it on Fluid compute. H3 supports Bun natively through its universal server layer, so the same code runs without changes. The Bun runtime is in public beta and supports most Node.js APIs. Set the major version only, and Vercel manages the minor and patch versions.

### Monitor performance with Observability

[Vercel Observability](https://vercel.com/docs/observability) tracks your deployed function automatically, with no setup. Open the Observability page in your project to see invocation counts, error rates, and duration for your H3 app, along with the requests your function makes to external APIs. On [Observability Plus](https://vercel.com/docs/observability/observability-plus), you also get longer retention and a latency breakdown by path.

## Best practices

### Export your app from a recognized entry point

Vercel finds your H3 app by looking for a file at a fixed set of locations: `app`, `index`, or `server` (with a `.js`, `.ts`, or related extension) at your project root or under `src/`. The file must export your app as a default export. Put your app at one of these paths so Vercel detects and deploys it correctly:

`import { H3 } from 'h3'; const app = new H3(); // Add your routes here export default app;`

### Handle errors by throwing HTTPError

H3 handles thrown errors for you and converts them into responses, so you don't add a trailing error-handling middleware the way you would in some Node frameworks. For controlled error responses, throw an `HTTPError` with the status and message you want. To log errors as they happen, add an `onError` hook:

``import { H3, HTTPError, onError } from 'h3'; const app = new H3(); app.use( onError((error, event) => { console.error(`${event.req.method} ${event.url.pathname}`, error); }), ); app.get('/posts/:id', (event) => { const post = null; // Look up your record here if (!post) { throw new HTTPError({ status: 404, message: 'Post not found' }); } return post; }); export default app;``

### Develop with the Vercel CLI

Run `vercel dev` for local development instead of starting the server with H3's `serve` helper. It serves your app the way production does, so the behavior you test locally matches what you deploy. This also lets you exercise features such as cron routes and the Bun runtime before shipping.

## Resources and next steps

- Read the [H3 documentation](https://h3.dev/) for framework details
  
- Browse [H3's built-in utilities](https://h3.dev/utils) for request parsing, cookies, and CORS
  
- Deploy the [H3 example](https://github.com/vercel/vercel/tree/main/examples/h3) or explore other [backends on Vercel](https://vercel.com/docs/frameworks/backend)
  
- Learn how [Vercel Functions](https://vercel.com/docs/functions) run your server code
  
- Understand pricing and scaling with [Fluid compute](https://vercel.com/docs/fluid-compute)
  
- Run code before requests with [Routing Middleware](https://vercel.com/docs/routing-middleware)
  
- Review the [Vercel Functions limits](https://vercel.com/docs/functions/limitations) that apply to your app
  
- Defer background work from your routes with [Vercel Queues](https://vercel.com/docs/queues), or orchestrate multi-step tasks with [Vercel Workflows](https://vercel.com/docs/workflows)

---

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