Blob
Use ViteHub Blob to store images, videos, documents, and other files. ViteHub detects the storage driver from your hosting environment automatically.
Getting started
Install the package
pnpm add https://pkg.pr.new/vite-hub/vitehub/@vitehub/blob@main
Choose an integration surface
export default defineNuxtConfig({
modules: ['@vitehub/blob/nuxt'],
})
Set a driver
ViteHub auto-configures blob storage from your environment or hosting provider. Keep the same top-level blob key no matter which integration surface you use.
.data/blob.Use the filesystem driver during local development or smoke testing.
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
modules: ['@vitehub/blob/nitro'],
blob: {
driver: 'fs',
dir: '.data/my-blob-directory',
},
})
The local filesystem driver is not suitable for production deployments.
:::
Install aws4fetch and set the S3 environment variables.
pnpm add aws4fetch
S3_ACCESS_KEY_ID=your-access-key-id
S3_SECRET_ACCESS_KEY=your-secret-access-key
S3_BUCKET=your-bucket-name
S3_REGION=eu-central-1
S3_ENDPOINT= # optional, required for S3-compatible services
Install @vercel/blob, then either deploy on Vercel or set BLOB_READ_WRITE_TOKEN locally.
pnpm add @vercel/blob
BLOB_READ_WRITE_TOKEN=your-token
ViteHub reads these values from process.env. Nuxt, Nitro, Vite, or your process manager must load .env before startup.
Files stored in Vercel Blob are public. Use another driver if you need private object storage.
Set the driver explicitly and provide the bucket name. ViteHub generates the Wrangler R2 binding for you.
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
modules: ['@vitehub/blob/nitro'],
blob: {
driver: 'cloudflare-r2',
bucketName: 'uploads',
jurisdiction: 'eu',
},
})
::
Upload a file
import { blob } from '@vitehub/blob'
export default defineEventHandler(async () => {
return await blob.put('avatars/user-1.txt', 'hello blob', {
contentType: 'text/plain',
})
})
::
Automatic configuration
Registering the integration without a blob config object keeps the setup small while following the same provider order as NuxtHub:
- Explicit
driver S3_*environment variables- Vercel hosting or
BLOB_READ_WRITE_TOKEN - Cloudflare hosting
- Local filesystem
S3_* env vars at runtime so masked serialized config does not override the real storage endpoint or credentials.Driver options
You can always pin a driver explicitly.
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
modules: ['@vitehub/blob/nitro'],
blob: {
driver: 'fs',
dir: '.data/files',
},
})
Docker and Kubernetes deployments
Build container images without blob credentials by deferring S3 environment resolution to runtime.
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
modules: ['@vitehub/blob/nitro'],
blob: {
driver: 's3',
},
})
Set the credentials when the container starts:
S3_ACCESS_KEY_ID=your-access-key-id
S3_SECRET_ACCESS_KEY=your-secret-access-key
S3_BUCKET=your-bucket-name
S3_REGION=eu-central-1
S3_ENDPOINT= # optional
S3_* first and BLOB_READ_WRITE_TOKEN second at runtime before using the filesystem driver.Nuxt Image integration
@nuxt/image can optimize images that you expose through a blob-backed route.
Install @nuxt/image
pnpm add @nuxt/image
Expose blob files on a route
Create a route that serves blobs through blob.serve():
import { createError, defineEventHandler, getRouterParam } from 'h3'
import { blob } from '@vitehub/blob'
export default defineEventHandler(async (event) => {
const pathname = getRouterParam(event, 'pathname')
if (!pathname)
throw createError({ statusCode: 404, statusMessage: 'Not Found' })
return await blob.serve(event, pathname)
})
Use a production image provider only in production
export default defineNuxtConfig({
modules: ['@vitehub/blob/nuxt'],
image: { provider: 'none' },
$production: {
image: {
provider: 'cloudflare',
},
},
})
provider: 'none' so routes like /images/** still resolve directly through your Nitro server.Serve blobs on a route
Control the content types you serve from user-generated blobs to avoid XSS issues.
Add a restrictive CSP header on the serving route when you only need raw file delivery:
import { createError, defineEventHandler, getRouterParam, setHeader } from 'h3'
import { blob } from '@vitehub/blob'
export default defineEventHandler(async (event) => {
const pathname = getRouterParam(event, 'pathname')
if (!pathname)
throw createError({ statusCode: 404, statusMessage: 'Not Found' })
setHeader(event, 'Content-Security-Policy', 'default-src \'none\';')
return await blob.serve(event, pathname)
})