Documentation Index Fetch the complete documentation index at: https://mintlify.com/supabase/supabase/llms.txt
Use this file to discover all available pages before exploring further.
Supabase Storage provides multiple methods to download and access files, whether they’re public or private.
Download Methods
Download as Blob
Download files as Blob objects for client-side processing:
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
console . log ( 'File size:' , data . size )
console . log ( 'MIME type:' , data . type )
// Create object URL for display
const url = URL . createObjectURL ( data )
document . getElementById ( 'image' ). src = url
}
Public URLs
For public buckets, get direct URLs:
const { data } = supabase
. storage
. from ( 'avatars' )
. getPublicUrl ( 'public/avatar1.png' )
console . log ( 'Public URL:' , data . publicUrl )
// Use in HTML
// <img src="{data.publicUrl}" alt="Avatar" />
Signed URLs
Generate temporary URLs for private files:
const { data , error } = await supabase
. storage
. from ( 'private-docs' )
. createSignedUrl ( 'document.pdf' , 60 ) // Expires in 60 seconds
if ( error ) {
console . error ( 'Error creating signed URL:' , error . message )
} else {
console . log ( 'Signed URL:' , data . signedUrl )
console . log ( 'Expires at:' , new Date ( data . expiresAt ))
}
Download Component
Complete React component for downloading files:
import { useState } from 'react'
import { supabase } from './supabaseClient'
export default function FileDownload ({ fileName , bucketName }) {
const [ downloading , setDownloading ] = useState ( false )
const downloadFile = async () => {
setDownloading ( true )
try {
const { data , error } = await supabase
. storage
. from ( bucketName )
. download ( fileName )
if ( error ) throw error
// Create blob URL
const url = URL . createObjectURL ( data )
// Create temporary link and trigger download
const link = document . createElement ( 'a' )
link . href = url
link . download = fileName
document . body . appendChild ( link )
link . click ()
document . body . removeChild ( link )
// Clean up
URL . revokeObjectURL ( url )
} catch ( error ) {
console . error ( 'Download failed:' , error )
alert ( 'Failed to download file' )
} finally {
setDownloading ( false )
}
}
return (
< button onClick = { downloadFile } disabled = { downloading } >
{ downloading ? 'Downloading...' : 'Download File' }
</ button >
)
}
Image Display
Display images from Storage:
import { useState , useEffect } from 'react'
import { supabase } from './supabaseClient'
export default function StorageImage ({ path , bucketName = 'avatars' }) {
const [ imageUrl , setImageUrl ] = useState ( null )
useEffect (() => {
async function getImage () {
// For public buckets
const { data } = supabase
. storage
. from ( bucketName )
. getPublicUrl ( path )
setImageUrl ( data . publicUrl )
}
if ( path ) {
getImage ()
}
}, [ path , bucketName ])
if ( ! imageUrl ) {
return < div > Loading... </ div >
}
return (
< img
src = { imageUrl }
alt = "Storage image"
style = { { maxWidth: '100%' } }
/>
)
}
Private Image Display
For private buckets, use signed URLs:
import { useState , useEffect } from 'react'
import { supabase } from './supabaseClient'
export default function PrivateImage ({ path , bucketName = 'private' }) {
const [ imageUrl , setImageUrl ] = useState ( null )
useEffect (() => {
async function getSignedUrl () {
const { data , error } = await supabase
. storage
. from ( bucketName )
. createSignedUrl ( path , 3600 ) // 1 hour expiry
if ( error ) {
console . error ( 'Error getting signed URL:' , error )
return
}
setImageUrl ( data . signedUrl )
}
if ( path ) {
getSignedUrl ()
}
}, [ path , bucketName ])
if ( ! imageUrl ) {
return < div > Loading... </ div >
}
return < img src = { imageUrl } alt = "Private image" />
}
List Files
Retrieve list of files in a bucket:
const { data , error } = await supabase
. storage
. from ( 'documents' )
. list ( 'user-123' , {
limit: 100 ,
offset: 0 ,
sortBy: { column: 'name' , order: 'asc' },
})
if ( error ) {
console . error ( 'Error listing files:' , error )
} else {
data . forEach ( file => {
console . log ( 'Name:' , file . name )
console . log ( 'Size:' , file . metadata . size )
console . log ( 'Updated:' , file . updated_at )
})
}
File Browser Component
import { useState , useEffect } from 'react'
import { supabase } from './supabaseClient'
export default function FileBrowser ({ bucketName , folder = '' }) {
const [ files , setFiles ] = useState ([])
const [ loading , setLoading ] = useState ( true )
useEffect (() => {
loadFiles ()
}, [ bucketName , folder ])
async function loadFiles () {
setLoading ( true )
const { data , error } = await supabase
. storage
. from ( bucketName )
. list ( folder , {
limit: 100 ,
offset: 0 ,
sortBy: { column: 'created_at' , order: 'desc' },
})
if ( error ) {
console . error ( 'Error loading files:' , error )
} else {
setFiles ( data )
}
setLoading ( false )
}
async function downloadFile ( fileName ) {
const filePath = folder ? ` ${ folder } / ${ fileName } ` : fileName
const { data , error } = await supabase
. storage
. from ( bucketName )
. download ( filePath )
if ( error ) {
alert ( 'Download failed: ' + error . message )
return
}
const url = URL . createObjectURL ( data )
const link = document . createElement ( 'a' )
link . href = url
link . download = fileName
link . click ()
URL . revokeObjectURL ( url )
}
if ( loading ) {
return < div > Loading files... </ div >
}
return (
< div >
< h2 > Files in { bucketName } / { folder || 'root' } </ h2 >
< table >
< thead >
< tr >
< th > Name </ th >
< th > Size </ th >
< th > Updated </ th >
< th > Actions </ th >
</ tr >
</ thead >
< tbody >
{ files . map (( file ) => (
< tr key = { file . name } >
< td > { file . name } </ td >
< td > { ( file . metadata ?. size / 1024 ). toFixed ( 2 ) } KB </ td >
< td > {new Date ( file . updated_at ). toLocaleDateString () } </ td >
< td >
< button onClick = { () => downloadFile ( file . name ) } >
Download
</ button >
</ td >
</ tr >
)) }
</ tbody >
</ table >
</ div >
)
}
Search Files
Search for files with specific patterns:
const { data , error } = await supabase
. storage
. from ( 'documents' )
. list ( 'reports' , {
search: '2024' ,
limit: 50 ,
sortBy: { column: 'name' , order: 'asc' },
})
const pdf Files = data . filter ( file => file . name . endsWith ( '.pdf' ))
console . log ( 'PDF files:' , pdfFiles )
Download Multiple Files
Download several files at once:
async function downloadMultipleFiles ( bucketName , filePaths ) {
const downloadPromises = filePaths . map ( async ( path ) => {
const { data , error } = await supabase
. storage
. from ( bucketName )
. download ( path )
if ( error ) {
console . error ( `Error downloading ${ path } :` , error )
return null
}
return { path , blob: data }
})
const results = await Promise . all ( downloadPromises )
return results . filter ( Boolean )
}
// Usage
const files = await downloadMultipleFiles ( 'documents' , [
'file1.pdf' ,
'file2.pdf' ,
'file3.pdf'
])
console . log ( `Downloaded ${ files . length } files` )
Stream Large Files
For very large files, stream the download:
async function streamDownload ( bucketName , filePath ) {
const { data } = supabase
. storage
. from ( bucketName )
. getPublicUrl ( filePath )
const response = await fetch ( data . publicUrl )
const reader = response . body . getReader ()
let receivedLength = 0
const chunks = []
while ( true ) {
const { done , value } = await reader . read ()
if ( done ) break
chunks . push ( value )
receivedLength += value . length
console . log ( `Received ${ receivedLength } bytes` )
}
const blob = new Blob ( chunks )
return blob
}
Download as Data URL
Convert downloaded file to base64 data URL:
async function getFileAsDataUrl ( bucketName , filePath ) {
const { data , error } = await supabase
. storage
. from ( bucketName )
. download ( filePath )
if ( error ) throw error
return new Promise (( resolve , reject ) => {
const reader = new FileReader ()
reader . onloadend = () => resolve ( reader . result )
reader . onerror = reject
reader . readAsDataURL ( data )
})
}
// Usage
const dataUrl = await getFileAsDataUrl ( 'avatars' , 'avatar.png' )
console . log ( dataUrl ) // data:image/png;base64,...
CDN Caching
Public URLs are automatically cached by Supabase’s CDN:
// URLs are cached based on the path
const { data } = supabase
. storage
. from ( 'images' )
. getPublicUrl ( 'logo.png' )
// To bust cache, add a query parameter
const urlWithCacheBust = ` ${ data . publicUrl } ?t= ${ Date . now () } `
Download with Progress
Track download progress:
async function downloadWithProgress ( bucketName , filePath , onProgress ) {
const { data : publicUrlData } = supabase
. storage
. from ( bucketName )
. getPublicUrl ( filePath )
const response = await fetch ( publicUrlData . publicUrl )
const contentLength = response . headers . get ( 'content-length' )
const reader = response . body . getReader ()
let receivedLength = 0
const chunks = []
while ( true ) {
const { done , value } = await reader . read ()
if ( done ) break
chunks . push ( value )
receivedLength += value . length
const progress = ( receivedLength / contentLength ) * 100
onProgress ( progress )
}
return new Blob ( chunks )
}
// Usage
const blob = await downloadWithProgress (
'videos' ,
'large-video.mp4' ,
( progress ) => console . log ( ` ${ progress . toFixed ( 2 ) } %` )
)
Get file information without downloading:
const { data , error } = await supabase
. storage
. from ( 'documents' )
. list ( '' , {
limit: 1 ,
search: 'report.pdf'
})
if ( data && data . length > 0 ) {
const file = data [ 0 ]
console . log ( 'Name:' , file . name )
console . log ( 'Size:' , file . metadata . size )
console . log ( 'MIME type:' , file . metadata . mimetype )
console . log ( 'Last modified:' , file . metadata . lastModified )
}
Error Handling
Common download errors:
Error Description Solution Object not foundFile doesn’t exist Verify file path Bucket not foundBucket doesn’t exist Check bucket name UnauthorizedNo access to private file Check RLS policies or use signed URL Resource not foundInvalid path Verify full path including folders
Best Practices
Use Public URLs For public files, use getPublicUrl for better performance
Cache Signed URLs Cache signed URLs client-side to reduce API calls
Clean Up Object URLs Always revoke object URLs with URL.revokeObjectURL()
Handle Large Files Use streaming or progress tracking for large downloads
Next Steps
Image Transformations Resize and optimize images on-the-fly
Access Control Secure files with RLS policies