Skip to main content
Supabase Storage provides a simple interface to store and serve large files. Built on top of PostgreSQL, it leverages Row Level Security for access control.

Features

Supabase Storage offers powerful file management capabilities:
  • Unlimited Storage: Store any number of files of any size
  • CDN Integration: Files are automatically cached for fast delivery
  • Image Transformations: Resize, crop, and optimize images on-the-fly
  • Resumable Uploads: Upload large files with automatic retry
  • Access Control: Secure files with Row Level Security policies
  • Public and Private Buckets: Control file visibility
  • S3 Compatible: Use S3-compatible tools and libraries

Core Concepts

Buckets

Buckets are containers for organizing files, similar to folders:
// Create a bucket
const { data, error } = await supabase
  .storage
  .createBucket('avatars', {
    public: true,
    fileSizeLimit: 1024 * 1024, // 1MB
  })

Objects

Objects are the individual files stored in buckets:
// Upload a file
const { data, error } = await supabase
  .storage
  .from('avatars')
  .upload('user-123.png', file)

Public vs Private Buckets

  • Public Buckets: Files are accessible via public URLs without authentication
  • Private Buckets: Files require authentication and RLS policies

Quick Start

Create a Bucket

  1. Go to Storage in your Supabase dashboard
  2. Click “New bucket”
  3. Enter a name and configure settings
  4. Click “Create bucket”

Upload a File

const file = event.target.files[0]

const { data, error } = await supabase
  .storage
  .from('avatars')
  .upload(`public/${file.name}`, file, {
    cacheControl: '3600',
    upsert: false
  })

if (error) {
  console.error('Upload error:', error.message)
} else {
  console.log('File uploaded:', data.path)
}

Get Public URL

const { data } = supabase
  .storage
  .from('avatars')
  .getPublicUrl('public/avatar1.png')

console.log('Public URL:', data.publicUrl)

Download a File

const { data, error } = await supabase
  .storage
  .from('avatars')
  .download('public/avatar1.png')

if (error) {
  console.error('Download error:', error.message)
} else {
  // data is a Blob
  const url = URL.createObjectURL(data)
  window.open(url)
}

Storage API

const { data, error } = await supabase
  .storage
  .from('avatars')
  .upload('space-cat.png', file)

File Organization

Organize files using folder structures:
// Upload to nested folder
await supabase
  .storage
  .from('documents')
  .upload('users/123/reports/2024-q1.pdf', file)

// List files in a folder
const { data } = await supabase
  .storage
  .from('documents')
  .list('users/123/reports')

File Metadata

Set custom metadata on uploads:
const { data, error } = await supabase
  .storage
  .from('avatars')
  .upload('avatar.png', file, {
    cacheControl: '3600',
    contentType: 'image/png',
    upsert: true,
  })

Storage Limits

Configure limits per bucket:
  • File Size Limit: Maximum size per file
  • Allowed MIME Types: Restrict file types
  • Total Bucket Size: Maximum total storage
const { data, error } = await supabase
  .storage
  .createBucket('uploads', {
    public: false,
    fileSizeLimit: 10485760, // 10MB
    allowedMimeTypes: [
      'image/png',
      'image/jpeg',
      'application/pdf'
    ],
  })

Security

RLS Policies for Storage

Control access to files using PostgreSQL policies:
-- Allow users to upload to their own folder
create policy "Users can upload to own folder"
on storage.objects
for insert
with check (
  bucket_id = 'avatars'
  and (storage.foldername(name))[1] = (select auth.uid()::text)
);

-- Allow users to read their own files
create policy "Users can read own files"
on storage.objects
for select
using (
  bucket_id = 'avatars'
  and (storage.foldername(name))[1] = (select auth.uid()::text)
);

Signed URLs

Generate temporary URLs for private files:
const { data, error } = await supabase
  .storage
  .from('private-documents')
  .createSignedUrl('contract.pdf', 60) // Expires in 60 seconds

if (data) {
  console.log('Signed URL:', data.signedUrl)
  console.log('Expires at:', data.expiresAt)
}

Use Cases

User Avatars

Store and serve user profile pictures with public access

Document Storage

Store private documents with user-specific access control

Media Library

Build a media library with image transformations

File Sharing

Share files via temporary signed URLs

Best Practices

Prevent overwrites by using UUIDs or timestamps in filenames:
const fileName = `${Date.now()}-${file.name}`
Configure appropriate cache control for better performance:
cacheControl: '3600' // 1 hour
Always validate file types on both client and server:
if (!file.type.startsWith('image/')) {
  throw new Error('Only images allowed')
}
Use folder structures to organize files logically:
avatars/
  users/
    {user-id}/
      profile.jpg

Monitoring

Track storage usage in your dashboard:
  • Total storage used
  • Files per bucket
  • Bandwidth consumption
  • API requests

Next Steps

Upload Files

Learn about different upload methods

Download Files

Retrieve and serve files

Image Transformations

Resize and optimize images

Access Control

Secure files with RLS policies