# Build with Vercel Blob on Nuxt

**Author:** Ben Sabic

---

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](https://vercel.com/storage/blob), Vercel's object storage service.

When you deploy to Vercel, [NuxtHub](https://hub.nuxt.com/) 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.

## Overview

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
  

## Prerequisites

- Node.js 22+ and a package manager (e.g., [pnpm](https://pnpm.io/))
  
- A [Vercel account](https://vercel.com/signup) with Vercel Blob
  
- A Nuxt project using NuxtHub
  

## Set up blob storage

### 1\. Enable the blob module

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

`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.

### 2\. Install the Vercel Blob SDK

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

### 3\. Create and connect a Blob store

From your Vercel project’s [storage settings](https://vercel.com/d?to=%2F%5Bteam%5D%2F%5Bproject%5D%2Fstores), 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.

## Run 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:

`BLOB_READ_WRITE_TOKEN=your_read_write_token_here`

Start the development server:

`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:

`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.

## Upload 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.

## Serve 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:

`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. ## Optimize images with Nuxt Image [Nuxt Image](https://image.nuxt.com/) (`@nuxt/image`) provides automatic image optimization, and you can combine it with your blob route to serve optimized images from storage.

### 1\. Install the module

`npx nuxt module add image`

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

### 2\. Configure the provider

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

`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/**`.

### 3\. Use the image components

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

`<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.

## Best practices

### Treat 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.

### Keep 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.

### Serve 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.

## Related resources and next steps

- Read the [Vercel Blob documentation](https://vercel.com/docs/vercel-blob) for storage concepts, caching, and more
  
- Explore the [@vercel/blob SDK reference](https://vercel.com/docs/vercel-blob/using-blob-sdk) for `put()`, `get()`, `list()`, and `del()`
  
- Learn more about [uploading files with NuxtHub](https://hub.nuxt.com/docs/blob/upload), including progress tracking
  
- See the [Nuxt Image Vercel provider docs](https://image.nuxt.com/providers/vercel) for optimization requirements and options

---

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