Storage
Provider-agnostic file upload, preview, and delete. Bytes go directly to a private bucket via short-lived presigned URLs. Cloudflare R2 is the first provider.
Tailar is a production-ready Next.js foundation. The CLI setup wizard composes templates, modules, and providers into code you own, including auth, billing, email, and database.
Last updated
Installation
Install the Package
Add Storage to your project using the Tailar CLI:
Set Environment Variables
Create a `.env` file in the root of your project and add the following environment variables:
1# Storage provider. Currently r2.2STORAGE_PROVIDER=r23 4# redirect: 302 to a short-lived presigned GET5# stream: proxy bytes through the Next.js route6STORAGE_DOWNLOAD_MODE=redirect7 8# Presigned PUT lifetime in seconds9STORAGE_UPLOAD_URL_EXPIRES_IN=30010 11# Presigned GET lifetime in seconds12STORAGE_DOWNLOAD_URL_EXPIRES_IN=6013 14# Cloudflare R2. Keep the bucket private (no public access, no r2.dev domain).15R2_ACCOUNT_ID=16R2_ACCESS_KEY_ID=17R2_SECRET_ACCESS_KEY=18R2_BUCKET=19 20# eu uses <ACCOUNT_ID>.eu.r2.cloudflarestorage.com. Use auto only for non-EU buckets.21R2_JURISDICTION=euPreview
Explore representative UI flows and snippets generated for this module (same previews as on the home page modules section).
Drop files here or click to browse
Images and documents up to 50 MB
report.pdf
1.2 MB · Uploading
avatar.png
240 KB
File Tree
The following files are installed when you add Storage:
1├─ components/2│ └─ storage/3│ ├─ file-upload.tsx4│ └─ file-browser.tsx5├─ lib/6│ └─ storage/7│ └─ index.ts8└─ app/9 └─ (protected)/10 └─ (app)/11 └─ files/12 └─ page.tsxUsage
Once you've installed Storage, you can start using the following actions:
createUpload
Authorize an upload and return a short-lived presigned PUT URL. The browser uploads bytes directly to the bucket, then calls finalizeUpload.
1import { createUpload, finalizeUpload } from '@/lib/storage'2 3const created = await createUpload({4 scope: 'files',5 fileName: file.name,6 contentType: file.type,7 size: file.size,8})9if (created.state === 'error') throw new Error(created.message)10 11await fetch(created.uploadUrl, {12 method: 'PUT',13 headers: created.headers,14 body: file,15})16await finalizeUpload(created.file.id)listFiles
List the signed-in user's READY files, optionally filtered by scope.
1import { listFiles } from '@/lib/storage'2 3const result = await listFiles('files')4if (result.state === 'success') {5 console.log(result.files)6}deleteFile
Soft-delete a file the current user owns and remove the object from storage.
1import { deleteFile } from '@/lib/storage'2 3const result = await deleteFile(fileId)4if (result.state === 'error') {5 console.error(result.message)6}FileUpload
Dropzone that uploads files for a scope, shows progress, and calls onReady when each file is stored.
1import { FileUpload } from '@/components/storage'2 3<FileUpload scope="files" onReady={(files) => console.log(files)} />References
For more information about Storage, check out these resources: