File uploads
The kit ships file uploads built in. /app/files has a dropzone and a file grid. Until you add
storage credentials it runs in demo mode — picked files are added to the list locally and
nothing is stored. Add an S3-compatible bucket and picks upload for real.
One config works with AWS S3, DigitalOcean Spaces, Cloudflare R2, or Supabase Storage — they all speak the S3 API. Uploads go through the app's server (browser → your server → bucket), so there is no bucket CORS to configure. Files are namespaced per signed-in user.
1. Add storage credentials
Copy this block into .env and fill in the values for your provider:
STORAGE_BUCKET=uploads
STORAGE_REGION=auto # AWS/DO/Supabase: real region · R2: auto
# STORAGE_ENDPOINT= # blank for AWS S3; set for DO / R2 / Supabase
STORAGE_ACCESS_KEY_ID=
STORAGE_SECRET_ACCESS_KEY=
STORAGE_FORCE_PATH_STYLE=false # true for Supabase Storage (and MinIO)
# STORAGE_PUBLIC_URL= # public/CDN base URL for reading files (optional)If any of STORAGE_BUCKET, STORAGE_ACCESS_KEY_ID, or STORAGE_SECRET_ACCESS_KEY is missing,
the kit stays in demo mode.
2. Per-provider settings
Fill the same keys differently depending on where your bucket lives:
| Provider | STORAGE_ENDPOINT | STORAGE_REGION | STORAGE_FORCE_PATH_STYLE |
|---|---|---|---|
| AWS S3 | (leave blank) | the bucket's real region, e.g. us-east-1 | false |
| Cloudflare R2 | https://<accountid>.r2.cloudflarestorage.com | auto | false |
| DigitalOcean Spaces | https://<region>.digitaloceanspaces.com | the Space's region, e.g. nyc3 | false |
| Supabase Storage | https://<project-ref>.storage.supabase.co/storage/v1/s3 | the project region | true |
Create the bucket in your provider's dashboard, generate an access key + secret, and paste them
into STORAGE_ACCESS_KEY_ID / STORAGE_SECRET_ACCESS_KEY.
3. Reading files back
- Private bucket (default): the kit generates short-lived signed URLs (about 1 hour) so the file grid can preview/download — nothing else is needed.
- Public bucket / CDN: set
STORAGE_PUBLIC_URLto your public base URL (for example a CloudFront or R2 public domain) and the grid links straight to it.
4. Test the setup
- Restart the dev server after editing
.env. - Open
/app/filesand drop a file onto the dropzone. - The file uploads through the server and appears in the grid.
- Check your provider's dashboard — the object is under
uploads/<your-user-id>/…. - Reload the page; the grid lists the file from storage (not the demo seed).
Production checklist
Before going live:
- Use a dedicated bucket and an access key scoped to just that bucket.
- Decide private (signed URLs) vs public (
STORAGE_PUBLIC_URL) and set it consistently. - Set a sensible max object size / lifecycle policy in your provider if you expect large files.
Useful docs: