Supabase Storage
Where files live in your app — profile photos, KYC documents, service images — with access controls that mirror your RLS rules.
Why First
Your platform needs to handle files. Partners uploading their Aadhaar card for KYC verification. Customers setting a profile photo. Service categories displaying images in the app.
Where do these files go?
Not in the database. A database column stores a number, a date, a piece of text — it is designed for structured data. If you put an image file into a database column, you are storing megabytes of binary data in a system built for kilobytes of structured values. It works, technically. It is slow, expensive, and the wrong tool.
Files live in a file system. Supabase Storage is that file system — managed, versioned, secure, and integrated with the same auth system your database uses. It gives each file a URL. It enforces who can read or upload each file. It scales automatically.
Excel analogy: Think of SharePoint or a network drive that has folders with different access levels. The Public folder is visible to everyone. The Confidential folder requires you to log in first. The HR Documents folder requires you to have the HR role. Supabase Storage works exactly like this — but for your app's files, controlled by the same login your users already have.
Navigating to Storage
In your Supabase dashboard:
- Click Storage in the left sidebar (the folder icon)
- You see the Buckets list — currently empty for a new project
Storage — the buckets list. The "New bucket" button is highlighted in the top right.
Creating a Bucket
A bucket is a named container for a category of files. Think of it as a top-level folder.
- Click New bucket
- Enter a bucket name — use lowercase letters and hyphens, no spaces (e.g.,
profile-photos,kyc-documents,service-images) - Choose Public or Private:
Public vs Private Buckets
| Public | Private | |
|---|---|---|
| Who can view files? | Anyone with the URL | Only authorised users (via signed URL) |
| How files are accessed | Direct URL: https://project.supabase.co/storage/v1/object/public/bucket-name/path | Temporary signed URL with expiry time |
| Good for | Profile photos, service images, logos, anything non-sensitive | KYC documents, invoices, contracts, personal files |
| Bad for | Sensitive personal information | Files that need to be publicly shareable forever |
- Click Save
Never put KYC documents, identity proofs, financial records, or personal information in a public bucket. Public means anyone who guesses or discovers the URL can download the file — no login required, no audit trail. Use a private bucket with signed URLs for any file that should only be accessible to its owner (and admins).
File Path Structure
When you upload a file to a bucket, it lives at a path — like a folder structure inside the bucket. Best practice:
Examples:
profile-photos/abc123-user-id/avatar.jpgkyc-documents/abc123-user-id/aadhaar-front.jpgservice-images/cleaning/hero.jpg
Using the user's ID as a folder name means you can write Storage policies that say "a user can only read/write files inside their own folder" — exactly like RLS for files.
Uploading Files via the Dashboard
For development and testing, you can upload files directly in the Supabase dashboard:
- Click into a bucket
- Click Upload files or drag and drop files onto the page
- Files appear in the file list with their path, size, and last modified time
- Click a file → Copy URL to get the file URL (public buckets give a permanent URL; private buckets generate a signed URL)
- Click Delete to remove a file permanently
In production, your app uploads files programmatically from the React frontend — covered in the Backend module.
Storage Policies (Access Control for Files)
Just like tables have RLS policies, buckets have Storage policies. They control who can upload, download, update, and delete files.
Storage policies — separate sections for SELECT (read), INSERT (upload), UPDATE, DELETE.
To set policies:
- In Storage, click your bucket name
- Click Policies tab
- You see separate sections for SELECT (read/download), INSERT (upload), UPDATE, and DELETE
The most useful patterns:
Allow users to read their own files:
storage.foldername(name) extracts the folder components from the file path. [1] gets the first component — the user ID folder. This checks that it matches the logged-in user.
Allow users to upload to their own folder:
Allow public read for a public bucket:
Getting File URLs in Code
You will use these two patterns constantly in your React app:
Public bucket — permanent URL:
Private bucket — signed URL (expires after set time):
Always use short expiry times for sensitive documents. For KYC files and financial documents, 1 hour (3600 seconds) is appropriate. For admin review workflows that take longer, generate a new signed URL on demand rather than extending the expiry. Permanent URLs for sensitive files are a compliance and security risk.
Standard Bucket Setup for EduTrack
| Bucket | Type | Purpose |
|---|---|---|
profile-photos | Public | Student and teacher avatar images |
kyc-documents | Private | Teacher Aadhaar, PAN, address proof |
result-documents | Public | Result cards and report PDFs |
fee-documents | Private | Parent-uploaded fee-related documents (e.g., scholarship certificates) |
Create all four buckets before starting the KYC or profile flows. Changing a bucket from public to private after files are uploaded requires migrating all existing URLs — avoid that by deciding the access level upfront.