vercel-blob
This skill provides comprehensive knowledge for integrating Vercel Blob object storage into Vercel applications. It should be used when setting up file uploads, managing images and documents, implementing CDN-delivered assets, or handling large files in Next.js serverless and edge functions. Use this skill when: - Setting up file uploads for Next.js applications (images, PDFs, videos) - Implementing client-side uploads with presigned URLs - Managing user-generated content (avatars, attachments, media) - Building file management features (list, download, delete) - Migrating from Cloudflare R2 to Vercel Blob - Encountering errors like "BLOB_READ_WRITE_TOKEN not set", "file size limit exceeded", or "client upload token errors" - Need simple object storage with automatic CDN distribution Keywords: vercel blob, @vercel/blob, vercel storage, vercel file upload, vercel cdn, blob storage vercel, client upload vercel, presigned url vercel, file upload nextjs, image upload vercel, pdf upload, video upload, user uploads, multipart upload, streaming upload, blob cdn, vercel assets, file download
What this skill does
# Vercel Blob (Object Storage) **Status**: Production Ready **Last Updated**: 2025-10-29 **Dependencies**: None **Latest Versions**: `@vercel/[email protected]` --- ## Quick Start (3 Minutes) ### 1. Create Blob Store ```bash # In Vercel dashboard: Storage → Create Database → Blob vercel env pull .env.local ``` Creates: `BLOB_READ_WRITE_TOKEN` ### 2. Install ```bash npm install @vercel/blob ``` ### 3. Upload File (Server Action) ```typescript 'use server'; import { put } from '@vercel/blob'; export async function uploadFile(formData: FormData) { const file = formData.get('file') as File; const blob = await put(file.name, file, { access: 'public' // or 'private' }); return blob.url; // https://xyz.public.blob.vercel-storage.com/file.jpg } ``` **CRITICAL:** - Use client upload tokens for direct client uploads (don't expose `BLOB_READ_WRITE_TOKEN`) - Set correct `access` level (`public` vs `private`) - Files are automatically distributed via CDN --- ## The 5-Step Setup Process ### Step 1: Create Blob Store **Vercel Dashboard**: 1. Project → Storage → Create Database → Blob 2. Copy `BLOB_READ_WRITE_TOKEN` **Local Development**: ```bash vercel env pull .env.local ``` Creates `.env.local`: ```bash BLOB_READ_WRITE_TOKEN="vercel_blob_rw_xxx" ``` **Key Points:** - Free tier: 100GB bandwidth/month - File size limit: 500MB per file - Automatic CDN distribution - Public files are cached globally --- ### Step 2: Server-Side Upload **Next.js Server Action:** ```typescript 'use server'; import { put } from '@vercel/blob'; export async function uploadAvatar(formData: FormData) { const file = formData.get('avatar') as File; // Validate file if (!file.type.startsWith('image/')) { throw new Error('Only images allowed'); } if (file.size > 5 * 1024 * 1024) { throw new Error('Max file size: 5MB'); } // Upload const blob = await put(`avatars/${Date.now()}-${file.name}`, file, { access: 'public', addRandomSuffix: false }); return { url: blob.url, pathname: blob.pathname }; } ``` **API Route (Edge Runtime):** ```typescript import { put } from '@vercel/blob'; export const runtime = 'edge'; export async function POST(request: Request) { const formData = await request.formData(); const file = formData.get('file') as File; const blob = await put(file.name, file, { access: 'public' }); return Response.json(blob); } ``` --- ### Step 3: Client-Side Upload (Presigned URLs) **Create Upload Token (Server Action):** ```typescript 'use server'; import { handleUpload, type HandleUploadBody } from '@vercel/blob/client'; export async function getUploadToken(filename: string) { const jsonResponse = await handleUpload({ body: { type: 'blob.generate-client-token', payload: { pathname: `uploads/${filename}`, access: 'public', onUploadCompleted: { callbackUrl: `${process.env.NEXT_PUBLIC_URL}/api/upload-complete` } } }, request: new Request('https://dummy'), onBeforeGenerateToken: async (pathname) => { // Optional: validate user permissions return { allowedContentTypes: ['image/jpeg', 'image/png', 'image/webp'], maximumSizeInBytes: 5 * 1024 * 1024 // 5MB }; }, onUploadCompleted: async ({ blob, tokenPayload }) => { console.log('Upload completed:', blob.url); } }); return jsonResponse; } ``` **Client Upload:** ```typescript 'use client'; import { upload } from '@vercel/blob/client'; import { getUploadToken } from './actions'; export function UploadForm() { async function handleSubmit(e: React.FormEvent<HTMLFormElement>) { e.preventDefault(); const form = e.currentTarget; const file = (form.elements.namedItem('file') as HTMLInputElement).files?.[0]; if (!file) return; const tokenResponse = await getUploadToken(file.name); const blob = await upload(file.name, file, { access: 'public', handleUploadUrl: tokenResponse.url }); console.log('Uploaded:', blob.url); } return ( <form onSubmit={handleSubmit}> <input type="file" name="file" required /> <button type="submit">Upload</button> </form> ); } ``` --- ### Step 4: List, Download, Delete **List Files:** ```typescript import { list } from '@vercel/blob'; const { blobs } = await list({ prefix: 'avatars/', limit: 100 }); // Returns: { url, pathname, size, uploadedAt, ... }[] ``` **List with Pagination:** ```typescript let cursor: string | undefined; const allBlobs = []; do { const { blobs, cursor: nextCursor } = await list({ prefix: 'uploads/', cursor }); allBlobs.push(...blobs); cursor = nextCursor; } while (cursor); ``` **Download File:** ```typescript // Files are publicly accessible if access: 'public' const url = 'https://xyz.public.blob.vercel-storage.com/file.pdf'; const response = await fetch(url); const blob = await response.blob(); ``` **Delete File:** ```typescript import { del } from '@vercel/blob'; await del('https://xyz.public.blob.vercel-storage.com/file.jpg'); // Or delete multiple await del([url1, url2, url3]); ``` --- ### Step 5: Streaming & Multipart **Streaming Upload:** ```typescript import { put } from '@vercel/blob'; import { createReadStream } from 'fs'; const stream = createReadStream('./large-file.mp4'); const blob = await put('videos/large-file.mp4', stream, { access: 'public', contentType: 'video/mp4' }); ``` **Multipart Upload (Large Files >500MB):** ```typescript import { createMultipartUpload, uploadPart, completeMultipartUpload } from '@vercel/blob'; // 1. Start multipart upload const upload = await createMultipartUpload('large-video.mp4', { access: 'public' }); // 2. Upload parts (chunks) const partSize = 100 * 1024 * 1024; // 100MB chunks const parts = []; for (let i = 0; i < totalParts; i++) { const chunk = getChunk(i, partSize); const part = await uploadPart(chunk, { uploadId: upload.uploadId, partNumber: i + 1 }); parts.push(part); } // 3. Complete upload const blob = await completeMultipartUpload({ uploadId: upload.uploadId, parts }); ``` --- ## Critical Rules ### Always Do ✅ **Use client upload tokens for client-side uploads** - Never expose `BLOB_READ_WRITE_TOKEN` to client ✅ **Set correct access level** - `public` (CDN) or `private` (authenticated access) ✅ **Validate file types and sizes** - Before upload, check MIME type and size ✅ **Use pathname organization** - `avatars/`, `uploads/`, `documents/` for structure ✅ **Handle upload errors** - Network failures, size limits, token expiration ✅ **Clean up old files** - Delete unused files to manage storage costs ✅ **Set content-type explicitly** - For correct browser handling (videos, PDFs) ### Never Do ❌ **Never expose `BLOB_READ_WRITE_TOKEN` to client** - Use `handleUpload()` for client uploads ❌ **Never skip file validation** - Always validate type, size, content before upload ❌ **Never upload files >500MB without multipart** - Use multipart upload for large files ❌ **Never use generic filenames** - `file.jpg` collides, use `${timestamp}-${name}` or UUID ❌ **Never assume uploads succeed** - Always handle errors (network, quota, etc.) ❌ **Never store sensitive data unencrypted** - Encrypt before upload if needed ❌ **Never forget to delete temporary files** - Old uploads consume quota --- ## Known Issues Prevention This skill prevents **10 documented issues**: ### Issue #1: Missing Environment Variable **Error**: `Error: BLOB_READ_WRITE_TOKEN is not defined` **Source**: https://vercel.com/docs/storage/vercel-blob **Why It Happens**: Token not set in environment **Prevention**: Run `vercel env pull .env.local` and ensure `.env.local` in `.gitignore`. ### Issue #2: Client Upload Token Exposed **Error**: Security vulnerability, unauthorized uploads **Source**: https://vercel.com/docs/storage/vercel-blob/client-upload **Why It Happens**: Using `BLOB_READ_WRITE_TOKEN` dire
Related in Image & Video
watch
IncludedWatch a video (URL or local path). Downloads with yt-dlp, extracts auto-scaled frames with ffmpeg, pulls the transcript from captions (or Whisper API fallback), and hands the result to Claude so it can answer questions about what's in the video.
physical-ai-defect-image-generation
IncludedUse when the user wants to orchestrate defect image generation, run associated setup, or handle outputs on OSMO. The Day 0 path handles cold-start with USD-to-ROI, image-edit augmentation, and AnomalyGen to create initial PCBA datasets. The Day 1 path performs inference and labeling on real images. This skill helps with first-time asset setup, creation of finetuning checkpoints, and configuring deployment. Trigger keywords: defect image generation, dig workflow, dig pipeline, defect image detection workflow, aoi pipeline, aoi anomalygen, usd2roi anomalygen, day 0 pcba, day 1 pcba, day 1 real-photo alignment, day 1 manual roi, metal surface anomaly, glass defect, anomalygen finetune, setup_pcb, setup_metal, setup_glass, setup_pretrained, dig setup, dig datasets, dig pretrained checkpoint, dig image-edit endpoint.
accelint-react-best-practices
IncludedReact performance optimization and best practices. ALWAYS use this skill when working with any React code - writing components, hooks, JSX; refactoring; optimizing re-renders, memoization, state management; reviewing for performance; fixing hydration mismatches; debugging infinite re-renders, stale closures, input focus loss, animations restarting; preventing remounting; implementing transitions, lazy initialization, effect dependencies. Even simple React tasks benefit from these patterns. Covers React 19+ (useEffectEvent, Activity, ref props). Triggers - useEffect, useState, useMemo, useCallback, memo, inline components, nested components, components inside components, re-render, performance, hydration, SSR, Next.js, useDeferredValue, combined hooks.
elevenlabs-agents
IncludedBuild conversational AI voice agents with ElevenLabs Platform using React, JavaScript, React Native, or Swift SDKs. Configure agents, tools (client/server/MCP), RAG knowledge bases, multi-voice, and Scribe real-time STT. Use when: building voice chat interfaces, implementing AI phone agents with Twilio, configuring agent workflows or tools, adding RAG knowledge bases, testing with CLI "agents as code", or troubleshooting deprecated @11labs packages, Android audio cutoff, CSP violations, dynamic variables, or WebRTC config. Keywords: ElevenLabs Agents, ElevenLabs voice agents, AI voice agents, conversational AI, @elevenlabs/react, @elevenlabs/client, @elevenlabs/react-native, @elevenlabs/elevenlabs-js, @elevenlabs/agents-cli, elevenlabs SDK, voice AI, TTS, text-to-speech, ASR, speech recognition, turn-taking model, WebRTC voice, WebSocket voice, ElevenLabs conversation, agent system prompt, agent tools, agent knowledge base, RAG voice agents, multi-voice agents, pronunciation dictionary, voice speed control, elevenlabs scribe, @11labs deprecated, Android audio cutoff, CSP violation elevenlabs, dynamic variables elevenlabs, case-sensitive tool names, webhook authentication
humanizer
IncludedHumanize AI-generated text by detecting and removing patterns typical of LLM output. Rewrites text to sound natural, specific, and human. Uses 28 pattern detectors, 560+ AI vocabulary terms across 3 tiers, and statistical analysis (burstiness, type-token ratio, readability) for comprehensive detection. Use when asked to humanize text, de-AI writing, make content sound more natural/human, review writing for AI patterns, score text for AI detection, or improve AI-generated drafts. Covers content, language, style, communication, and filler categories.
generating-mermaid-diagrams
IncludedSalesforce architecture diagrams using Mermaid with ASCII fallback. Use this skill when generating text-based diagrams for Salesforce architecture, OAuth flows, ERDs, integration sequences, or Agentforce structure. TRIGGER when: user says "diagram", "visualize", "ERD", or asks for sequence diagrams, flowcharts, class diagrams, or architecture visualizations in Mermaid. DO NOT TRIGGER when: user wants PNG/SVG image output (use generating-visual-diagrams), or asks about non-Salesforce systems.