
Convex File Storage
Implement Convex-backed upload flows, file URLs, generated blobs from actions, and deletion in a solo SaaS backend.
Overview
Convex File Storage is an agent skill for the Build phase that implements complete file handling on Convex—upload flows, serving via URL, action-generated files, and deletion.
Install
npx skills add https://github.com/waynesutton/convexskills --skill convex-file-storageWhat is this skill?
- End-to-end Convex file handling: client upload flows, storage IDs, and serving files via URL
- Patterns for storing files generated inside Convex actions
- Deletion and lifecycle cleanup aligned with Convex file storage APIs
- Designed as the file-storage slice of the Convex skills cluster for agent-driven implementation
Adoption & trust: 2.1k installs on skills.sh; 402 GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are building on Convex but lack a coherent pattern for uploads, public or signed URLs, files created in actions, and safe deletion.
Who is it for?
Indie SaaS backends on Convex that need user uploads, generated reports, or media served from the same database project.
Skip if: Projects not on Convex or teams that only need static assets on a CDN with no Convex storage integration.
When should I use this skill?
Implementing or debugging Convex file upload, URL serving, action-generated files, or deletion in your app backend.
What do I get? / Deliverables
Your agent implements Convex-aligned storage flows end to end so user files and generated assets are stored, served, and removed predictably.
- Working upload and serve flow wired to Convex storage
- Action paths that persist generated files
- Deletion or cleanup handlers for stored objects
Recommended Skills
Journey fit
File storage is product backend work during implementation, not distribution or ops tuning as the primary shelf. Backend is the right facet for Convex storage APIs, mutations, and serving files—not generic agent-tooling or frontend-only polish.
How it compares
Convex-native storage skill—not a generic S3 CLI recipe or a separate MCP file server.
Common Questions / FAQ
Who is convex-file-storage for?
Solo and small-team builders using Convex who want agent-guided implementation of storage uploads, URLs, action outputs, and deletion.
When should I use convex-file-storage?
During Build while wiring backend file features—profile images, attachments, PDF exports from actions, or cleanup jobs—before Ship security review of upload surfaces.
Is convex-file-storage safe to install?
Implementations typically need network and Convex credentials in your project; review the Security Audits panel on this Prism page and your env handling before install.
SKILL.md
READMESKILL.md - Convex File Storage
interface: icon_small: "./assets/small-logo.svg" icon_large: "./assets/large-logo.png" <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> <g clip-path="url(#clip0_3_23)"> <g clip-path="url(#clip1_3_23)"> <path d="M10.0643 12.5735C12.3769 12.3166 14.5572 11.0843 15.7577 9.02756C15.1892 14.1148 9.62646 17.3302 5.08583 15.356C4.66743 15.1746 4.30728 14.8728 4.06013 14.4848C3.03973 12.8825 2.7043 10.8437 3.18626 8.99344C4.56327 11.37 7.3632 12.8267 10.0643 12.5735Z" fill="#F3B01C"/> <path d="M3.1018 7.50072C2.16436 9.66714 2.12376 12.2034 3.27303 14.2907C-0.771507 11.2479 -0.72737 4.7362 3.2236 1.72378C3.58904 1.44535 4.02333 1.2801 4.47881 1.25494C6.3519 1.15614 8.25501 1.88006 9.58963 3.22909C6.87799 3.25604 4.23695 4.99308 3.1018 7.50072Z" fill="#8D2676"/> <path d="M10.8974 3.89562C9.52924 1.98794 7.38779 0.68921 5.04156 0.649695C9.57686 -1.40888 15.1555 1.92867 15.7629 6.86314C15.8194 7.32119 15.7452 7.78824 15.5421 8.20138C14.6948 9.92223 13.1236 11.2569 11.2876 11.7508C12.6328 9.25579 12.4668 6.20748 10.8974 3.89562Z" fill="#EE342F"/> </g> </g> <defs> <clipPath id="clip0_3_23"> <rect width="16" height="16" fill="white"/> </clipPath> <clipPath id="clip1_3_23"> <rect width="16" height="16" fill="white"/> </clipPath> </defs> </svg> --- name: convex-file-storage displayName: Convex File Storage description: Complete file handling including upload flows, serving files via URL, storing generated files from actions, deletion, and accessing file metadata from system tables version: 1.0.0 author: Convex tags: [convex, file-storage, uploads, images, files] --- # Convex File Storage Handle file uploads, storage, serving, and management in Convex applications with proper patterns for images, documents, and generated files. ## Documentation Sources Before implementing, do not assume; fetch the latest documentation: - Primary: https://docs.convex.dev/file-storage - Upload Files: https://docs.convex.dev/file-storage/upload-files - Serve Files: https://docs.convex.dev/file-storage/serve-files - For broader context: https://docs.convex.dev/llms.txt ## Instructions ### File Storage Overview Convex provides built-in file storage with: - Automatic URL generation for serving files - Support for any file type (images, PDFs, videos, etc.) - File metadata via the `_storage` system table - Integration with mutations and actions ### Generating Upload URLs ```typescript // convex/files.ts import { mutation } from "./_generated/server"; import { v } from "convex/values"; export const generateUploadUrl = mutation({ args: {}, returns: v.string(), handler: async (ctx) => { return await ctx.storage.generateUploadUrl(); }, }); ``` ### Client-Side Upload ```typescript // React component import { useMutation } from "convex/react"; import { api } from "../convex/_generated/api"; import { useState } from "react"; function FileUploader() { const generateUploadUrl = useMutation(api.files.generateUploadUrl); const saveFile = useMutation(api.files.saveFile); const [uploading, setUploading] = useState(false); const handleUpload = async (e: React.ChangeEvent<HTMLInputElement>) => { const file = e.target.files?.[0]; if (!file) return; setUploading(true); try { // Step 1: Get upload URL const uploadUrl = await generateUploadUrl(); // Step 2: Upload file to storage const result = await fetch(uploadUrl, { method: "POST", headers: { "Content-Type": file.type }, body: file, }); const { storageId } = await result.json(); // Step 3: Save file reference to database await saveFile({ storageId, fileName: file.name, fileType: file.type, fileSize: file.size, }); } finally { setUploading(false); } }; return ( <div> <input type="file" onChange={handleUpload} disabled={uploading} /> {uploading && <p>Uploa