Nuxt Module
File Storage
Enable Convex storage scaffolding in nuxt-convex and use the Nuxt storage helpers in app code.
Use this guide when you want the module to scaffold the base Convex storage functions and expose the Nuxt storage helpers.
Before you begin
Make sure:
- the core module is already installed
- your app can resolve
#convex/api - you have run
npx convex devin the project that owns theconvex/directory
Enable storage
Turn on the storage feature in nuxt.config.ts.
nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-convex'],
convex: {
storage: true,
},
})
When storage: true is enabled, nuxt-convex:
- adds the
#convex/storagealias - auto-imports
useConvexStorageanduseConvexUpload - scaffolds
convex/_hub/storage.tsif it does not already exist
Review the generated scaffold
The scaffolded file is the starting point for the storage API.
convex/_hub/storage.ts
import { v } from 'convex/values'
import { mutation, query } from '../_generated/server'
export const generateUploadUrl = mutation(async (ctx) => {
return await ctx.storage.generateUploadUrl()
})
export const getUrl = query({
args: { storageId: v.id('_storage') },
handler: async (ctx, { storageId }) => {
return await ctx.storage.getUrl(storageId)
},
})
export const remove = mutation({
args: { storageId: v.id('_storage') },
handler: async (ctx, { storageId }) => {
await ctx.storage.delete(storageId)
},
})
This scaffold only covers upload URLs, public URLs, and deletion. Add your own metadata table and mutation when you need filenames, MIME types, ownership, or domain-specific rules.
Upload a file from Nuxt
Use useConvexUpload for the upload flow and your own mutation to persist metadata.
app/components/FileUpload.vue
<script setup lang="ts">
import { api } from '#convex/api'
const { mutate: saveUpload } = useConvexMutation(api.files.saveUpload)
const { upload, isUploading, error } = useConvexUpload({
onSuccess: async (storageId, file) => {
await saveUpload({
storageId,
name: file.name,
type: file.type,
})
},
})
async function onSelect(event: Event) {
const file = (event.target as HTMLInputElement).files?.[0]
if (file)
await upload(file)
}
</script>
Verify the result
The setup is working when:
- the module generates
convex/_hub/storage.ts useConvexUploadis auto-imported- a selected file uploads successfully and your follow-up mutation stores the metadata you expect
Next steps
- Read Cloudflare R2 if you want bucket-backed uploads instead of Convex storage
- Read
useConvexStoragefor the low-level storage helper - Read
useConvexUploadfor the upload wrapper API