To integrate Filestack with V0 by Vercel, generate a file upload UI with V0, embed the Filestack Picker widget using your publishable API key on the client side, and create a Next.js API route to handle server-side transformations and secure uploads. Files are uploaded directly to Filestack's CDN, then your backend processes metadata and transformations.
Add Powerful File Upload and Media Management to V0 Apps with Filestack
Filestack solves one of the most tedious problems in web development: letting users upload files from multiple sources — their computer, Google Drive, Dropbox, Instagram, webcam, and direct URLs — without building each integration yourself. The Filestack Picker is a polished, production-ready file selection widget that handles the entire upload experience, including progress indicators, drag-and-drop, file previews, and source switching. Once a file is uploaded, Filestack delivers it from a CDN and provides a URL you can store, display, or transform on the fly.
For V0-generated apps, Filestack's integration pattern is unique because the actual file upload happens client-side directly to Filestack's infrastructure — your Next.js app never receives the file bytes. Instead, the Picker widget handles the upload and returns a CDN handle (a URL like https://cdn.filestackcontent.com/abc123) to a callback in your component. Your app stores this handle and uses it to reference the file. This is excellent for serverless Vercel deployments because you avoid the 4.5MB request body limit that would otherwise constrain file uploads through API routes.
Filestack's transformation API is a powerful differentiator: you can resize, crop, compress, watermark, rotate, and convert images simply by modifying the CDN URL. For example, adding /resize=width:800/auto_image/ to a Filestack URL returns a resized and format-optimized version. Video files can be transcoded, audio can be converted, and documents can be converted to PDF — all without any server-side processing code. V0 can generate image gallery components, avatar upload flows, and document management UIs that leverage these transformations through URL composition.
Integration method
Filestack integrates with V0-generated Next.js apps through a dual-layer pattern: the Filestack Picker widget runs client-side using your publishable API key to handle direct-to-CDN file uploads, while a Next.js API route handles server-side operations like signed URL generation, transformation parameters, and secure upload policies. The client-side Picker handles the user-facing file selection experience across local files, Google Drive, Dropbox, and URLs, then delivers a CDN URL for each uploaded file. Transformation operations are applied via URL parameters on the CDN URL.
Prerequisites
- A Filestack account — sign up at filestack.com (free tier available with 500 uploads/month and 5GB bandwidth)
- Your Filestack API Key — found in the Filestack Dashboard under API Keys (this is a publishable key safe for client-side use)
- Your Filestack Secret — found in the Filestack Dashboard (needed only for server-side signed operations, keep this server-only)
- A V0 account at v0.dev for generating the upload UI and a Vercel account for deployment
- Basic understanding of React hooks — the Filestack Picker uses callbacks that you handle in useEffect or event handlers
Step-by-step guide
Generate the File Upload UI with V0
Generate the File Upload UI with V0
Open V0 at v0.dev and describe the file upload interface you want to build. Filestack integrations usually involve a trigger button or drop zone that opens the Picker, a progress indicator during upload, and a display area for uploaded file previews or a file list table. When prompting V0, be specific about the file types you accept (images, documents, any), the number of files (single or multiple), and how uploaded files should be displayed afterward. V0 generates React components with shadcn/ui — it will generate Button components for the upload trigger, Card components for the file list, and Progress components for upload feedback. The Filestack Picker is initialized in client-side JavaScript, so the upload trigger component should be a Client Component with the 'use client' directive. Tell V0 what happens after upload succeeds — whether you store the CDN URL in state, POST it to an API route, or navigate to a new page. After generating the UI, you will need to add the Filestack SDK initialization code (covered in the next step), but V0 gives you the structural scaffold and styling. Push the generated component to GitHub using V0's Git panel.
Create a file upload page for a project management app. Include a large dashed upload zone with a cloud icon, 'Drag files here or click to browse' text, and an 'Upload from Google Drive or Dropbox' secondary button. Below the upload zone, show an uploaded files list with file name, size, a thumbnail for images (or a document icon for other types), an upload status badge (Uploading/Complete/Error), and a 'Remove' button. Include a file count and total size display at the bottom of the list. Show a submit button that POSTs the list of file handles to /api/uploads/save. Use a clean white background with blue accent colors.
Paste this in V0 chat
Pro tip: Ask V0 to generate a separate FilePicker component that accepts an onUploadDone callback prop — this makes the Filestack Picker initialization self-contained and reusable across multiple pages in your app.
Expected result: A file upload interface renders in V0's preview with a styled drop zone, file list table, and action buttons. The component structure is ready to receive Filestack Picker callbacks.
Install Filestack SDK and Initialize the Picker
Install Filestack SDK and Initialize the Picker
Install the Filestack JavaScript SDK and integrate it into your V0-generated upload component. The SDK provides the PickerInstance that opens the Filestack Picker modal with your API key and configuration options. Since the Picker uses browser APIs, the component must be a Client Component. The filestack-js package is the official npm SDK — install it with npm install filestack-js. The Picker configuration accepts many options including fromSources (which upload sources to show: 'local_file_system', 'googledrive', 'dropbox', 'url', 'webcam', 'instagram'), accept (file type restrictions), maxFiles (upload limit), maxSize (file size limit in bytes), and transformations (enable the built-in transformation UI). The onUploadDone callback receives an array of file objects, each with a handle (the unique Filestack file identifier), url (the full CDN URL), filename, mimetype, and size. The CDN URL format is https://cdn.filestackcontent.com/{handle} and you can append transformation parameters like /resize=width:800/auto_image/ between the base URL and the handle to apply transformations. Store the returned handles and URLs in your component state and send them to your backend API route for persistence in your database. For V0-generated apps using Supabase or another database, store the CDN URL string rather than the file bytes — Filestack is your file storage layer.
Update the file upload component to use the Filestack SDK. Add 'use client' at the top. Import { init } from 'filestack-js'. On button click, call init(process.env.NEXT_PUBLIC_FILESTACK_API_KEY).picker({ fromSources: ['local_file_system', 'googledrive', 'dropbox'], accept: ['image/*', 'application/pdf'], maxFiles: 10, onUploadDone: (result) => handleUploadDone(result.filesUploaded) }).open(). In handleUploadDone, update the file list state with the returned files (each file has url, filename, mimetype, size). Show each image file as a thumbnail using the returned url prop.
Paste this in V0 chat
1// components/FilestackUploader.tsx2'use client';34import { useState } from 'react';5import { Button } from '@/components/ui/button';6import { Card } from '@/components/ui/card';7import { Badge } from '@/components/ui/badge';89interface UploadedFile {10 handle: string;11 url: string;12 filename: string;13 mimetype: string;14 size: number;15}1617interface FilestackUploaderProps {18 onFilesUploaded: (files: UploadedFile[]) => void;19 maxFiles?: number;20 accept?: string[];21}2223export function FilestackUploader({24 onFilesUploaded,25 maxFiles = 5,26 accept = ['image/*', 'application/pdf'],27}: FilestackUploaderProps) {28 const [uploadedFiles, setUploadedFiles] = useState<UploadedFile[]>([]);29 const [isLoading, setIsLoading] = useState(false);3031 const openPicker = async () => {32 const apiKey = process.env.NEXT_PUBLIC_FILESTACK_API_KEY;33 if (!apiKey) {34 console.error('NEXT_PUBLIC_FILESTACK_API_KEY is not set');35 return;36 }3738 setIsLoading(true);3940 // Dynamically import to avoid SSR issues41 const { init } = await import('filestack-js');42 const client = init(apiKey);4344 const picker = client.picker({45 fromSources: ['local_file_system', 'googledrive', 'dropbox', 'url'],46 accept,47 maxFiles,48 onUploadDone: (result: { filesUploaded: UploadedFile[] }) => {49 const newFiles = result.filesUploaded;50 setUploadedFiles((prev) => [...prev, ...newFiles]);51 onFilesUploaded(newFiles);52 setIsLoading(false);53 },54 onFileUploadFailed: () => {55 setIsLoading(false);56 },57 onClose: () => {58 setIsLoading(false);59 },60 });6162 picker.open();63 };6465 const removeFile = (handle: string) => {66 setUploadedFiles((prev) => prev.filter((f) => f.handle !== handle));67 };6869 // Generate Filestack transformation URL for image thumbnails70 const getThumbnailUrl = (url: string, width = 200) => {71 return url.replace(72 'https://cdn.filestackcontent.com/',73 `https://cdn.filestackcontent.com/resize=width:${width}/auto_image/`74 );75 };7677 return (78 <div className="space-y-4">79 <Button onClick={openPicker} disabled={isLoading} className="w-full">80 {isLoading ? 'Opening picker...' : 'Upload Files'}81 </Button>8283 {uploadedFiles.length > 0 && (84 <div className="space-y-2">85 {uploadedFiles.map((file) => (86 <Card key={file.handle} className="p-3 flex items-center gap-3">87 {file.mimetype.startsWith('image/') && (88 <img89 src={getThumbnailUrl(file.url)}90 alt={file.filename}91 className="w-12 h-12 object-cover rounded"92 />93 )}94 <div className="flex-1 min-w-0">95 <p className="text-sm font-medium truncate">{file.filename}</p>96 <p className="text-xs text-muted-foreground">97 {(file.size / 1024).toFixed(1)} KB98 </p>99 </div>100 <Badge variant="secondary">Uploaded</Badge>101 <Button102 variant="ghost"103 size="sm"104 onClick={() => removeFile(file.handle)}105 >106 Remove107 </Button>108 </Card>109 ))}110 </div>111 )}112 </div>113 );114}Pro tip: Use dynamic import for the Filestack SDK (await import('filestack-js')) to avoid Next.js SSR errors — the Picker uses browser APIs that aren't available during server rendering.
Expected result: Clicking the Upload Files button opens the Filestack Picker modal. After selecting and uploading files, they appear in the component list with thumbnails for image files and the CDN URLs are passed to the onFilesUploaded callback.
Create a Server-Side Route for Signed Upload Policies
Create a Server-Side Route for Signed Upload Policies
For production applications, Filestack recommends using signed upload policies to prevent unauthorized uploads to your account. Without a policy, anyone who discovers your API key can upload files to your Filestack storage at your expense. The signing process happens server-side using your Filestack Secret (which must never reach the browser) and creates a time-limited, cryptographically signed policy that the Picker uses to authenticate uploads. Create a Next.js API route that generates a signed policy on demand. The policy specifies what operations are allowed, which file types are accepted, the maximum file size, and an expiry timestamp. The signing uses HMAC-SHA256 with your Filestack Secret key and returns a handle, policy (base64-encoded JSON), and signature that the Picker sends with each upload request. Add the signature generation to your API route using Node.js's built-in crypto module — no additional dependencies required. Your upload component fetches this policy from /api/filestack/policy before opening the Picker and passes the policy and signature to the Picker configuration. This two-step flow (fetch policy, then open picker) is the recommended production pattern. You can also use this API route to validate file handles received from the client before storing them in your database, ensuring files were actually uploaded through your approved Picker configuration.
1// app/api/filestack/policy/route.ts2import { NextResponse } from 'next/server';3import { createHmac } from 'crypto';45export async function GET() {6 const secret = process.env.FILESTACK_SECRET;7 if (!secret) {8 return NextResponse.json({ error: 'Filestack not configured' }, { status: 500 });9 }1011 // Policy expires in 1 hour12 const expiry = Math.floor(Date.now() / 1000) + 3600;1314 const policy = {15 expiry,16 call: ['pick', 'read', 'stat', 'write', 'writeUrl'],17 maxSize: 20971520, // 20MB max file size18 };1920 const policyBase64 = Buffer.from(JSON.stringify(policy)).toString('base64');21 const signature = createHmac('sha256', secret)22 .update(policyBase64)23 .digest('hex');2425 return NextResponse.json({26 policy: policyBase64,27 signature,28 apiKey: process.env.NEXT_PUBLIC_FILESTACK_API_KEY,29 });30}Pro tip: Cache the signed policy in your component state for the duration of the upload session — policies are valid for up to one hour, so there's no need to fetch a new one for each file if the user uploads multiple files in sequence.
Expected result: GET /api/filestack/policy returns a base64-encoded policy and HMAC signature that the Filestack Picker uses to authenticate uploads with your account credentials.
Add Vercel Environment Variables and Deploy
Add Vercel Environment Variables and Deploy
Configure Filestack credentials in Vercel before deploying your app. Open the Vercel Dashboard, navigate to your project, and go to Settings → Environment Variables. Add NEXT_PUBLIC_FILESTACK_API_KEY with your Filestack API Key — this key uses the NEXT_PUBLIC_ prefix because it is safe for client-side use and required by the Picker widget running in the browser. Add FILESTACK_SECRET with your Filestack app secret — this must NOT have the NEXT_PUBLIC_ prefix since it is used only in the server-side policy signing API route. The secret must remain server-only. Set both variables for Production, Preview, and Development environments, then click Save. For local development, add both variables to .env.local in your project root. After setting variables, trigger a Vercel redeployment from the Deployments tab. Once deployed, test file uploads through the live URL by opening the Picker and uploading a test image. Verify the CDN URL returned is accessible and that transformations work by appending /resize=width:400/ to the URL in your browser. If uploads succeed but you see 400 errors on the policy route, check that FILESTACK_SECRET matches the secret in your Filestack Dashboard exactly — extra spaces or newlines in the environment variable value are a common source of signing failures.
Pro tip: Test Filestack image transformations directly in the browser by modifying the CDN URL: take a returned file URL like https://cdn.filestackcontent.com/abc123 and change it to https://cdn.filestackcontent.com/resize=width:400,fit:crop/output=format:webp/abc123 to get a 400px-wide WebP crop.
Expected result: The Vercel deployment succeeds. File uploads through the Picker create CDN-hosted files accessible via Filestack URLs. Image transformations apply correctly via URL parameter manipulation.
Common use cases
Profile Avatar Upload with Automatic Resizing
A user profile settings page where users can upload a profile photo from their computer or link a Google Drive image. The Filestack Picker handles the upload and returns a CDN URL, which is then transformed via URL parameters to generate resized avatar variants (64px, 128px, 256px) for use throughout the app.
Create a user profile settings page with an avatar upload section. Show the current avatar in a circular 128px frame with an 'Edit photo' overlay button. Clicking the button opens the Filestack Picker (using window.filestack.init). After upload, display the returned CDN URL as the new avatar and show a 'Save changes' button. Include name, email, and bio fields alongside the avatar. On save, POST the avatar URL and profile data to /api/profile/update. Use a clean settings page layout with a white card and subtle dividers.
Copy this prompt to try it in V0
Document Upload Dashboard for Client Files
A client document management interface where users can upload contracts, invoices, and reports from multiple sources including Google Drive and Dropbox. Uploaded files are listed in a sortable table with filename, size, upload date, and a preview link that opens the Filestack CDN URL.
Build a document management dashboard with a drag-and-drop upload zone and a 'Upload from cloud' button that opens the Filestack Picker. Show an upload progress bar during upload. Below the upload area, display a table of uploaded documents with columns for file name, file type icon, size, upload date, uploader name, and action buttons (Preview, Download, Delete). Documents load from /api/documents. Include a search input to filter by filename and a filter by file type (PDF/Image/Spreadsheet). Use a professional design with blue accent color.
Copy this prompt to try it in V0
Product Image Gallery with CDN Transformations
An e-commerce product editing form where sellers can upload multiple product images. Filestack handles multi-file uploads and returns CDN handles for each. The app stores the handles and uses Filestack's transformation URLs to generate thumbnail variants for the product listing and full-size versions for the product detail page.
Design a product image editor with a multi-image upload area using Filestack Picker (allow up to 10 files, images only). Display uploaded images in a draggable grid for reordering. Each image shows a delete button and a 'Set as primary' button. The primary image gets a star badge overlay. Show a 'Save product images' button that POSTs the array of Filestack CDN handles to /api/products/images. Include a thumbnail preview column showing the image at 200px width. Use an e-commerce admin aesthetic with clean cards.
Copy this prompt to try it in V0
Troubleshooting
Filestack Picker fails to open with 'window is not defined' error during build
Cause: The filestack-js SDK uses browser APIs that are not available during Next.js server-side rendering. If the SDK is imported at the module level in a Server Component, Next.js attempts to run it during build.
Solution: Use dynamic import inside the onClick handler (await import('filestack-js')) rather than a top-level import, and ensure the component using the Picker has the 'use client' directive at the top of the file.
1// In your client component:2const openPicker = async () => {3 const { init } = await import('filestack-js'); // Dynamic import avoids SSR4 const client = init(process.env.NEXT_PUBLIC_FILESTACK_API_KEY!);5 client.picker({ /* config */ }).open();6};Uploaded files show 403 Forbidden when accessing the CDN URL
Cause: The Filestack security policy has expired or the handle is from a different app's API key. Filestack security policies create time-limited access — handles remain permanent, but access can be restricted by policy settings.
Solution: Check that the CDN URL you're generating matches the API key in your account. If you're using signed policies, ensure the policy includes the 'read' call in the policy's call array. Temporarily disable security (remove policy/signature from the Picker) to confirm the base upload is working.
NEXT_PUBLIC_FILESTACK_API_KEY is undefined in the deployed app but works locally
Cause: The NEXT_PUBLIC_ environment variable was not set in Vercel before the deployment, or the app was not redeployed after adding the variable. NEXT_PUBLIC_ variables are inlined at build time, so a deployment before the variable is set will have undefined.
Solution: Go to Vercel Dashboard → Settings → Environment Variables and confirm NEXT_PUBLIC_FILESTACK_API_KEY is set. Then go to Deployments → Redeploy to trigger a fresh build that includes the variable. Check that the variable name is exactly NEXT_PUBLIC_FILESTACK_API_KEY with no typos.
Best practices
- Use dynamic import for the Filestack SDK to prevent server-side rendering errors — the Picker uses browser-only APIs that fail during Next.js build
- Always use signed upload policies in production to prevent unauthorized users from uploading files to your Filestack account at your expense
- Store Filestack CDN handles (the short identifier like abc123) rather than full URLs in your database — you can always reconstruct the URL, but handles are more compact and allow URL template changes
- Use Filestack's automatic format conversion (auto_image transformation) to serve WebP to modern browsers and JPEG to older ones automatically via URL parameters
- Set reasonable maxSize limits in your Picker configuration to prevent accidentally large uploads that could exhaust your monthly bandwidth quota
- Add FILESTACK_SECRET to Vercel without the NEXT_PUBLIC_ prefix — exposing your secret key would allow anyone to bypass upload policies and access all files in your account
- For image-heavy apps, leverage Filestack's transformation URL chaining to generate all size variants from a single uploaded file rather than storing multiple copies
Alternatives
Use AWS S3 instead of Filestack if you need raw object storage with granular access control and are comfortable writing your own upload handling — S3 is more flexible but requires significantly more configuration code.
Choose Box instead of Filestack if your use case involves enterprise document management and collaboration rather than media transformations — Box has stronger compliance controls and approval workflows.
Use Dropbox instead of Filestack if your users already store files in Dropbox and need bidirectional sync — Dropbox's API supports reading and writing to user-owned storage, while Filestack focuses on upload and transformation.
Frequently asked questions
Does Filestack store the files or do I need my own storage?
Filestack provides built-in CDN storage included with all plans — uploaded files are stored and served from Filestack's CDN by default. You can also configure Filestack to deliver files to your own S3 bucket, Google Cloud Storage, or Azure Blob Storage through the Intelligent Ingestion feature if you prefer to own the underlying storage.
Can Filestack upload files larger than Vercel's 4.5MB request limit?
Yes — this is one of Filestack's main advantages. Because files are uploaded directly from the browser to Filestack's CDN (not through your Next.js API route), there is no server-side file size constraint. Vercel's 4.5MB limit only applies to requests your API routes receive. Filestack supports files up to multiple gigabytes depending on your plan.
How do I apply image transformations like resizing and compression?
Filestack transformations are applied by inserting transformation parameters into the CDN URL. For example, to get a 400px wide, auto-compressed WebP version of a file, use: https://cdn.filestackcontent.com/resize=width:400/output=format:webp/{handle}. Transformations can be chained with slashes. The full list of transformations is available in the Filestack documentation.
What file sources does the Filestack Picker support?
The Picker supports local computer files, Google Drive, Dropbox, OneDrive, Box, Instagram, Facebook, GitHub, webcam capture, screen capture, and direct URL import. You configure which sources appear in the Picker via the fromSources array in the Picker configuration. Users can switch between sources in a single upload session.
Is Filestack free to use for development and testing?
Filestack offers a free tier with 500 uploads per month, 5GB of monthly bandwidth, and 500MB of storage. This is sufficient for development and small production apps. Paid plans start at $49/month for higher limits. A free account does not require a credit card and is a good way to test the integration before committing.
How do I delete files from Filestack storage?
File deletion requires a server-side API call using your Filestack Secret. Create an API route that accepts a file handle and calls the Filestack REST API's delete endpoint with your API key and security policy. Clients should never be able to delete files directly — always route deletion through a server-side API route that validates the user is authorized to delete the specific file.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation