Skip to content
Docs

Build with Vercel Blob on Nuxt

Set up Vercel Blob in a Nuxt application with NuxtHub, upload and serve files, and deliver optimized images with Nuxt Image.

Ben SabicContent Engineer

Handling user file uploads usually means provisioning a storage bucket, wiring up credentials, and writing upload logic before you can ship anything. In a Nuxt application, the NuxtHub blob module removes that setup by providing a single storage API that supports Vercel Blob, Vercel's object storage service.

When you deploy to Vercel, NuxtHub configures the Vercel Blob driver for you, so the same blob.put() and blob.serve() calls work in development and production without code changes. This guide walks through enabling blob storage in your Nuxt project, running it locally, serving stored files through a route you control, and optimizing images with Nuxt Image.

Copy link to headingOverview

In this guide, you'll learn how to:

  • Enable blob storage in a Nuxt project and connect it to Vercel Blob
  • Run the project locally with the right environment variables
  • Upload files and serve them through a server route
  • Deliver optimized images from blob storage with Nuxt Image

Copy link to headingPrerequisites

  • Node.js 22+ and a package manager (e.g., pnpm)
  • A Vercel account with Vercel Blob
  • A Nuxt project using NuxtHub

Copy link to headingSet up blob storage

Copy link to heading1. Enable the blob module

Enable blob storage in your project by setting blob: true in the NuxtHub config:

nuxt.config.ts
export default defineNuxtConfig({
hub: {
blob: true
}
})

NuxtHub configures the blob storage driver automatically based on your environment variables or hosting provider. If it can't detect a driver, files are stored locally in the .data/blob directory, which works for development but isn't suitable for production.

Copy link to heading2. Install the Vercel Blob SDK

The Vercel Blob driver depends on the @vercel/blob package:

Terminal
pnpm i @vercel/blob
Terminal
npm i @vercel/blob
Terminal
yarn add @vercel/blob
Terminal
bun add @vercel/blob

Copy link to heading3. Create and connect a Blob store

From your Vercel project’s storage settings, assign a Blob store to the project. Vercel configures the store's credentials as environment variables in your project, and NuxtHub automatically picks up the Vercel Blob driver on deploy.

Files stored through the Vercel Blob driver are always public. If you're storing sensitive files, configure a different storage driver instead.

Copy link to headingRun the project locally

The Vercel Blob driver reads a BLOB_READ_WRITE_TOKEN environment variable when running locally. Copy the token from your Blob store's settings in the dashboard and add it to your .env file:

.env
BLOB_READ_WRITE_TOKEN=your_read_write_token_here

Start the development server:

Terminal
pnpm dev

With the token set, uploads from your local machine go to the same Blob store as production. Without it, NuxtHub falls back to the local filesystem driver and writes files to .data/blob.

You can also pin the driver explicitly instead of relying on auto-detection. The $production key lets you keep the filesystem driver in development and switch to Vercel Blob in production:

nuxt.config.ts
export default defineNuxtConfig({
hub: {
blob: {
driver: 'fs',
dir: '.data/blob'
}
},
$production: {
hub: {
blob: {
driver: 'vercel-blob'
}
}
}
})

The vercel-blob driver also accepts a token option, which defaults to the BLOB_READ_WRITE_TOKEN environment variable.

Copy link to headingUpload a file

Import the blob client from hub:blob and call put() with a pathname and the file data:

import { blob } from 'hub:blob'
const file = await blob.put('avatars/user-1.png', imageData)

The pathname becomes the file's address in the store, so a consistent naming scheme such as avatars/{userId}.png keeps uploads organized and predictable. You can browse uploaded files from your Vercel dashboard by opening the project's Storage tab and selecting the Blob store.

Copy link to headingServe blobs through a route

Create a server route that streams stored files to the browser using blob.serve(). This route puts your application in the request path for every download, which gives you a place to set headers or add access checks:

server/routes/images/[...pathname].get.ts
import { blob } from 'hub:blob'
import { createError, eventHandler, getRouterParam } from 'h3'
export default eventHandler(async (event) => {
const pathname = getRouterParam(event, 'pathname')
if (!pathname) {
throw createError({ statusCode: 404, statusMessage: 'Not Found' })
}
return blob.serve(event, pathname)
})

The catch-all [...pathname] segment maps a request like /images/avatars/user-1.png to the blob stored at avatars/user-1.png. This route should exist in both development and production. In production, Vercel optimizes image delivery by generating URLs that wrap this route.

Copy link to headingOptimize images with Nuxt Image

Nuxt Image (@nuxt/image) provides automatic image optimization, and you can combine it with your blob route to serve optimized images from storage.

Copy link to heading1. Install the module

Terminal
npx nuxt module add image

This installs @nuxt/image and adds it to the modules section of your nuxt.config.ts.

Copy link to heading2. Configure the provider

Use the Vercel provider in production only, and disable optimization in development:

nuxt.config.ts
export default defineNuxtConfig({
image: { provider: 'none' },
$production: {
image: {
provider: 'vercel'
}
}
})

The Vercel provider generates /_vercel/image URLs that only exist on Vercel deployments, so enabling it in development produces broken images. Setting provider: 'none' in development keeps src="/images/..." paths pointing at your blob route directly.

Avoid the default ipx provider for blob-backed images. IPX generates /_ipx/... URLs and tries to read the source from the filesystem, so it fails with IPX_FILE_NOT_FOUND errors when the source is a blob route like /images/**.

Copy link to heading3. Use the image components

Use <NuxtImg> or <NuxtPicture> to display optimized images from blob storage:

pages/gallery.vue
<template>
<NuxtImg src="/images/photo.jpg" width="300" quality="80" />
</template>

The src path combines your route prefix (/images) with the blob pathname (photo.jpg). In production, Vercel fetches the source through your route, optimizes it, and caches the result on the CDN.

Copy link to headingBest practices

Copy link to headingTreat stored files as public

Every file stored through the Vercel Blob driver is publicly accessible. Store only content that anyone may view, such as avatars, gallery images, or marketing assets. For sensitive files, configure a different storage driver with access controls that match your requirements.

Copy link to headingKeep environment-specific config in $production

Both the blob driver and the image provider behave differently between development and production. Grouping the production values under the $production key keeps one config file working across both environments and prevents production-only URLs, such as /_vercel/image, from breaking local development.

Copy link to headingServe blobs through your own route

Even though files in the store are public, routing downloads through blob.serve() gives you one place to control response headers, content security policies, and future access rules. It's also the integration point that lets Nuxt Image and Vercel's image optimization work with your stored files.

Copy link to headingFAQ

What is Vercel Blob?

Vercel Blob is an object storage service for uploading and serving files of any size, from avatars to multi-terabyte videos. Files are delivered through Vercel's CDN via unique URLs, and stores can be public or private. It's a Vercel-native alternative to external storage like Amazon S3.

How much does Vercel Blob cost?

Vercel Blob is free on the Hobby plan, subject to usage limits. Pro teams pay for storage, operations, uploads, and data transfer through their monthly credit allocation and then on demand. Enterprise pricing is custom; contact your account team for more information.

What's the difference between public and private Blob stores?

Public blobs are accessible to anyone with the URL, and browsers fetch them directly from the CDN. Private blobs require authentication for every read and write, so you serve them through a Vercel Function that fetches the blob with get() and streams it to the user. You choose the access mode when creating the store and cannot change it afterwards.

What is the maximum file size for Vercel Blob?

Vercel Blob supports files up to 5 TB using multipart uploads. Set multipart: true on put() or upload() and the SDK splits the file into parts, uploads them in parallel, and retries failed parts. Multipart uploads are recommended for files larger than 100 MB.

How do I upload files larger than 4.5 MB?

Use client uploads. Vercel Functions have a 4.5 MB request body limit, so server uploads can't accept larger files. With client uploads, the file goes directly from the browser to Vercel Blob, secured by a token exchange with your server through the handleUpload() helper.

How many Vercel Blob stores can I create, and what does creation cost?

You can create unlimited Vercel Blob stores on every plan. Each store creation counts as one Blob Advanced Operation, priced at $5 per million on Pro and counted toward Hobby’s 2,000 free monthly operations. Deleting a store is free. Separate stores can isolate environments, customers, or temporary preview and migration data.

Related documentation

More Vercel Blob guides