Vercel Blob Storage lets you upload and serve files from anywhere, but it’s not just a CDN for static assets; it’s a fully integrated file store that can be manipulated programmatically.
Let’s see it in action. Imagine you have a Next.js application where users can upload profile pictures.
// app/api/upload/route.ts
import { PutBlobResult, put } from '@vercel/blob';
import { NextResponse } from 'next/server';
export async function POST(request: Request): Promise<NextResponse> {
const file = request.body; // This is the incoming file stream
if (!file) {
return NextResponse.json({ error: 'No file provided' }, { status: 400 });
}
// Define a unique filename, perhaps based on user ID or a timestamp
const filename = `profile-pictures/${Date.now()}.jpg`;
try {
const blob = await put(filename, file, {
access: 'public', // 'public' or 'private'
contentType: 'image/jpeg', // Important for correct serving
// token: YOUR_BLOB_UPLOAD_TOKEN // For authenticated uploads
});
return NextResponse.json(blob);
} catch (error) {
console.error('Upload failed:', error);
return NextResponse.json({ error: 'File upload failed' }, { status: 500 });
}
}
And here’s how you’d serve that image in your React component:
// app/page.tsx
import { getBlob, getSignedUrl } from '@vercel/blob';
import Image from 'next/image';
// Assuming you have a way to get the blob URL, e.g., from your database
// For demonstration, let's use a placeholder
const userProfilePictureUrl = 'https://your-deployment-name.vercel.app/api/_blob/your-blob-id.jpg';
async function ProfilePicture() {
// If the blob is private, you'd fetch a signed URL
// const blob = await getBlob(userProfilePictureUrl); // For public blobs
// const signedUrl = await getSignedUrl({ url: userProfilePictureUrl, expires: 60 * 5 }); // 5 minutes expiry
return (
<div>
<h2>User Profile</h2>
{/* For public blobs */}
<Image
src={userProfilePictureUrl}
alt="User Profile Picture"
width={100}
height={100}
/>
{/* For private blobs using signed URL */}
{/* <Image src={signedUrl} alt="User Profile Picture" width={100} height={100} /> */}
</div>
);
}
export default ProfilePicture;
The core problem Vercel Blob solves is decoupling file storage from your application’s deployment. Instead of bundling large assets with your code or relying on separate, unmanaged storage solutions, Blob integrates seamlessly. When you put a file, it’s stored in a dedicated, globally distributed object store managed by Vercel. When you serve it, Vercel’s edge network efficiently delivers it to users. This means faster load times for your users and simpler deployments for you, as your application code remains lean.
Internally, put is an API call to Vercel’s Blob service. It takes a filename, the file content (as a stream, buffer, or file object), and configuration options. The access option determines whether the blob is publicly accessible via a direct URL or requires a signed URL for private access, which is generated using getSignedUrl. The contentType is crucial for the browser to correctly interpret and render the file. The token parameter in put is used for authenticated uploads, allowing your backend to securely authorize file uploads from your frontend.
The magic of Blob isn’t just storage; it’s also transformation. You can leverage URLs to perform on-the-fly image resizing, cropping, and format conversion. For instance, to get a 200x200 thumbnail of your-blob-id.jpg, you’d simply append transformations to the URL:
https://your-deployment-name.vercel.app/_blob/your-blob-id.jpg?w=200&h=200&fit=cover
This URL-based transformation means you don’t need to store multiple versions of an image. The original is stored, and derivatives are generated and cached by Vercel’s CDN as needed. This dramatically reduces storage costs and simplifies asset management.
What most people don’t realize is that the put function can accept a contentDisposition option. Setting this to 'attachment; filename="your-download.pdf"' will trigger a download prompt in the browser with the specified filename when the user accesses the blob’s URL, rather than attempting to display it inline. This is incredibly useful for serving generated reports or downloadable assets.
The next step is understanding how to manage and delete blobs, especially when dealing with user-generated content that needs to be removed.