Add File Uploads to Any AI-Built Website or Web App With Supabase

Let users upload real images or documents with Supabase Storage, using the right access setup for public or private files.

Roadmap & Resources

Choose the Upload Setup

Sign-in comes first

If users will upload files from inside your app, they should already be able to sign in. If your app does not have authentication yet, complete the Supabase authentication guide first.

Add User Authentication to Any Website with Supabase

Builds sign up, login, sessions, and protected areas with Supabase.

/video/add-user-authentication-supabase

Choose Who Can Open the Files

Pick this once. Steps 2, 4 and 5 follow your answer.

Anyone with the link — good for public images, downloadable resources, public attachments, or other files that do not need to stay private. Only the signed-in user — good for personal documents, private attachments, or files that belong to one account.

Who should be able to open uploaded files?

Anyone with the link

Good for public images, downloadable resources, or public attachments.

Public files

Only the signed-in user

Good for personal documents, private attachments, or files that belong to one account.

Private files

Plan the Upload Feature

Plan the File Upload

Tell AI what users will upload and where the upload should appear in your app.

I want to add a file upload feature to this existing project using Supabase Storage. Users will upload: {{UPLOAD_PURPOSE}} The upload should appear here: {{UPLOAD_LOCATION}} Allowed file types: {{FILE_TYPES}} Maximum file size: {{MAX_FILE_SIZE}} File access: {{selection:fileAccess|Not chosen yet — ask me whether uploaded files should be openable by anyone who has the file link, or only by the signed-in user who uploaded them.}} "Anyone with the link" means a public Supabase Storage bucket; "Only the signed-in user" means a private bucket. Before changing anything, inspect the current project and plan the smallest upload workflow that fits it. Check: - The current framework - The existing Supabase client - The existing authentication setup - Where the upload UI belongs - How the uploaded file should be displayed or downloaded afterward The users uploading files must already be authenticated. Do not create authentication in this task. Do not create anonymous upload access. Do not modify files yet. Recommend: - A simple bucket name - Sensible file type restrictions - A sensible maximum file size - A safe per-user file path convention - Which existing project files will likely need to change Keep the plan simple. Do not add unrelated storage features. Explain the plan in beginner-friendly language before making changes. If the project does not currently have working user authentication, stop and tell me to add authentication before continuing rather than opening Storage writes anonymously.

What are users uploading?

Profile images, project attachments, PDFs, product photos...

Where should the upload appear?

Account settings, project page, product editor...

What file types should be allowed?

Maximum file size

Review the upload plan before continuing. Start with one upload feature rather than building a complete file manager.

Create the Storage Setup

Create the Storage Bucket

Use the bucket name and the limits from your plan.

Open your Supabase project Open Storage Create one new bucket using the name from the plan Apply the file size limit and the allowed file types from the plan

Create the bucket as Public. Files in a public bucket can be opened by anyone who has their URL. Uploading will still be restricted to signed-in users.

Keep the bucket Private — that is the default. Private files stay behind access rules and should not use permanent public URLs.

Prepare Safe Storage Access

File access

Follows the option you chose in Step 1. Changing it here changes it everywhere in this guide.

Anyone with the link

Create the minimum Storage policies needed for signed-in users to upload and access their own files.

Public files

Set Up Storage Access

Using the Storage plan we already confirmed, prepare the minimum Supabase Storage access rules needed for this upload feature. The files are stored in a public bucket, so retrieval happens through the public bucket. Uploading must still be restricted to signed-in users. Users uploading files are already authenticated. Use a per-user folder structure based on the authenticated user's ID. A user must not be able to upload into another user's folder. Allow only the operations this guide actually needs. For this setup: - Allow authenticated users to upload only into their own folder - Do not add anonymous upload permission - Public file retrieval is handled by the public bucket, so do not add policies for it - If authenticated listing of the user's own uploaded files needs a SELECT policy in this implementation, scope that access to the user's own folder - Do not grant UPDATE or DELETE permissions unless the upload experience genuinely uses them - Do not use upsert by default Do not open anonymous INSERT access. Do not use a Secret key in browser code. Do not disable RLS. Do not create service-role browser access. Do not create unrelated policies. If the existing Storage setup conflicts with the plan, explain the conflict before replacing policies. Generate one SQL block I can run in the Supabase SQL Editor and briefly explain what it allows. Use current Supabase Storage policy conventions. Do not modify application code yet.

Only the signed-in user

Create the minimum Storage policies needed for signed-in users to upload and access their own files.

Private files

Set Up Storage Access

Using the Storage plan we already confirmed, prepare the minimum Supabase Storage access rules needed for this upload feature. The files are stored in a private bucket, so every read must go through the Storage access rules. Users uploading files are already authenticated. Use a per-user folder structure based on the authenticated user's ID. A user must not be able to upload into another user's folder. Allow only the operations this guide actually needs. For this setup: - Allow authenticated users to upload only into their own folder - Allow authenticated users to read only their own files - Scope every policy so one account cannot browse another account's folder - Do not add public read access - Do not grant UPDATE or DELETE permissions unless the upload experience genuinely uses them - Do not use upsert by default Do not open anonymous INSERT access. Do not use a Secret key in browser code. Do not disable RLS. Do not create service-role browser access. Do not create unrelated policies. If the existing Storage setup conflicts with the plan, explain the conflict before replacing policies. Generate one SQL block I can run in the Supabase SQL Editor and briefly explain what it allows. Use current Supabase Storage policy conventions. Do not modify application code yet.

Run the Storage SQL

You do not need to read the SQL — just run it.

Open the SQL Editor in your Supabase project Create a new query Paste the generated Storage policy SQL Run it and confirm it completes without errors

Add the Upload Feature

Build the Upload

Add the File Upload

Add the upload UI and connect it to Supabase Storage without changing the rest of the app.

Implement the file upload feature we planned using the existing Supabase connection and authentication. Add it only where we agreed it belongs. Requirements: - Use the currently signed-in user's ID - Upload into that user's own Storage folder - Generate a unique file path/name so one upload does not accidentally overwrite another - Keep upsert disabled unless there is a specific existing replacement workflow that requires it - Validate the selected file type - Enforce the planned maximum file size in the UI - Respect the bucket's own file restrictions as the real backend limit - Show a clear uploading state - Show a clear success state - Show a useful error message if the upload fails - Prevent duplicate submissions while an upload is in progress - Preserve the current design and layout - Follow the project's existing component patterns Do not: - Add anonymous upload access - Use a Supabase Secret key in browser code - Disable Storage RLS - Add unrelated features - Redesign the page - Build a general-purpose file manager After a successful upload, keep the returned Storage path available so the app can display or retrieve the file in the next step. Do not permanently store a short-lived signed URL as if it were the file's permanent identity. The durable value is the Storage object path. If the existing app already has a database record that this upload belongs to, use the project's existing data model appropriately rather than creating a new unrelated database system. If no database association is needed for this simple feature, do not invent one. When finished, tell me: - Which files changed - The Storage path format used - Where file type and size validation happen - How upload errors are shown Do not make unrelated improvements.

Display or Download the Uploaded File

Use the Uploaded File

File access

Follows the option you chose in Step 1. Changing it here changes it everywhere in this guide.

Anyone with the link

Use the uploaded Storage path to show or download the public file in the app.

Public files

Show the Uploaded File

Finish the public-file workflow. The upload is stored in a public Supabase Storage bucket. Use the Storage object path returned by the upload and generate the public URL using the normal Supabase Storage client API. Requirements: - Use the existing Supabase client - Use the correct bucket - Use the uploaded object path - Generate the public URL rather than manually hardcoding the Storage URL - Display the file appropriately for the existing interface - For images, show the image where appropriate - For documents or other files, provide an appropriate open or download action - Preserve the existing design - Handle a missing path cleanly Remember: a public bucket means anyone who has the resulting file URL may retrieve the asset. Do not add a Secret key. Do not add signed URLs to this public-file path. Do not redesign the app. Do not add unrelated features. When finished, tell me how the app turns the Storage path into the public file URL.

Only the signed-in user

Let the signed-in user open their private upload without exposing it through a permanent public URL.

Private files

Show the Private File

Finish the private-file workflow. The upload is stored in a private Supabase Storage bucket. Use the Storage object path and the current authenticated Supabase session to let the owning user access the file. Use the simplest current Supabase approach appropriate for this project: - Authenticated file access, or - A short-lived signed URL when the interface needs a URL for preview, open or download Requirements: - The user must only access files allowed by the Storage policies - Do not convert the bucket to public - Do not construct a permanent public URL - Do not expose a Supabase Secret key - Do not bypass Storage RLS - If using a signed URL, generate it when needed and give it a reasonable short lifetime - Treat the Storage object path as the durable file reference, not the signed URL - Display or download the file appropriately for the existing interface - Handle expired or failed access cleanly - Preserve the current design Do not add unrelated features. When finished, explain briefly how the private file is retrieved without making it public.

Test the Upload

Test It Yourself

Run the real thing once, with a real file.

Sign in to the app

Choose an allowed test file

Upload it and confirm the upload completes

Confirm the file appears in Supabase Storage

Confirm the app can show, open, or download it

Try a disallowed file type or an oversized file and confirm it is rejected cleanly

Open the resulting public file URL and confirm the file loads as expected.

Confirm the signed-in owner can open the file. Then sign out and confirm the app does not expose that file through a permanent public URL.

Verify the File Upload

File access

Follows the option you chose in Step 1. Changing it here changes it everywhere in this guide.

Anyone with the link

Check that uploads work and that public or private file access matches the option selected for this guide.

Public files

Verify the Upload Setup

Perform a focused verification of the Supabase Storage upload feature we just added. Verify: - Uploading requires an authenticated user - The file uploads into the correct bucket - The path is scoped to the signed-in user - File names/paths do not accidentally overwrite existing uploads - File type restrictions work - File size restrictions work - Uploading, success, and error states behave correctly - No Supabase Secret key is exposed to browser code - Storage RLS has not been disabled - The implementation does not give anonymous users write access Then verify the chosen access model: - The bucket is intentionally public - The uploaded file can be retrieved through its public URL - Upload permission is still restricted even though reads are public Do not broaden this into a full Supabase security audit. Do not create a new test suite. Do not redesign anything. Do not add unrelated features. If something in this upload workflow is genuinely broken, fix only that issue. When finished, report: - Whether upload works - Which access model is active - What was verified - Whether any directly related issue remains

Only the signed-in user

Check that uploads work and that public or private file access matches the option selected for this guide.

Private files

Verify the Upload Setup

Perform a focused verification of the Supabase Storage upload feature we just added. Verify: - Uploading requires an authenticated user - The file uploads into the correct bucket - The path is scoped to the signed-in user - File names/paths do not accidentally overwrite existing uploads - File type restrictions work - File size restrictions work - Uploading, success, and error states behave correctly - No Supabase Secret key is exposed to browser code - Storage RLS has not been disabled - The implementation does not give anonymous users write access Then verify the chosen access model: - The bucket remains private - The owning user can access the file - An unauthenticated visitor cannot retrieve it through a permanent public URL - Private access uses authenticated access or a short-lived signed URL - The app does not permanently save a signed URL as the file reference Do not broaden this into a full Supabase security audit. Do not create a new test suite. Do not redesign anything. Do not add unrelated features. If something in this upload workflow is genuinely broken, fix only that issue. When finished, report: - Whether upload works - Which access model is active - What was verified - Whether any directly related issue remains

Upload one more small test file yourself. If it uploads and opens with the access level you selected, the file-upload workflow is working.