supabase-storage
Expert guide for Supabase Storage including bucket management, file operations, URL generation, and RLS policies. Use when working with file uploads/downloads, creating public or private buckets, generating signed URLs, implementing storage RLS policies, handling resumable uploads, image transformations, or any Supabase Storage-related tasks.
What this skill does
# Supabase Storage Expert
You are an expert in Supabase Storage, specializing in file management, bucket configuration, access control, and Row Level Security (RLS) policies for storage objects.
## 1. Storage Architecture Overview
Supabase Storage provides S3-compatible object storage with built-in CDN, image transformations, and Row Level Security integration.
**Key Components:**
- **Buckets**: Containers for organizing files (public or private)
- **Objects**: Files stored within buckets
- **RLS Policies**: Security rules controlling access to buckets and objects
- **Helper Functions**: SQL functions for path manipulation and access control
---
## 2. Bucket Management
### Creating Buckets
#### JavaScript
```javascript
const { data, error } = await supabase.storage.createBucket('avatars', {
public: true,
allowedMimeTypes: ['image/*'],
fileSizeLimit: '1MB',
})
```
#### SQL
```sql
insert into storage.buckets (id, name, public)
values ('avatars', 'avatars', true);
```
#### Config.toml (Local Development)
```toml
[storage.buckets.images]
public = false
file_size_limit = "50MiB"
allowed_mime_types = ["image/png", "image/jpeg"]
objects_path = "./images"
```
### Bucket Operations
**Get Bucket:**
```javascript
const { data, error } = await supabase.storage.getBucket('avatars')
```
**List Buckets:**
```javascript
const { data, error } = await supabase.storage.listBuckets()
```
**Update Bucket:**
```javascript
const { data, error } = await supabase.storage.updateBucket('avatars', {
public: false,
allowedMimeTypes: ['image/png', 'image/jpeg'],
fileSizeLimit: '5MB'
})
```
**Delete Bucket:**
```javascript
const { data, error } = await supabase.storage.deleteBucket('avatars')
```
**Empty Bucket:**
```javascript
const { data, error } = await supabase.storage.emptyBucket('avatars')
```
---
## 3. File Operations
### Upload Files
#### Standard Upload
```javascript
const avatarFile = event.target.files[0]
const { data, error } = await supabase
.storage
.from('avatars')
.upload('public/avatar1.png', avatarFile, {
cacheControl: '3600',
upsert: false
})
```
#### Upload from Base64 (React Native)
```javascript
import { decode } from 'base64-arraybuffer'
const { data, error } = await supabase
.storage
.from('avatars')
.upload('public/avatar1.png', decode('base64FileData'), {
contentType: 'image/png'
})
```
#### TypeScript Upload Function
```typescript
async function uploadAvatar(file: File, userId: string) {
const fileExt = file.name.split('.').pop()
const fileName = `${userId}-${Math.random()}.${fileExt}`
const filePath = `avatars/${fileName}`
const { data, error } = await supabase.storage
.from('avatars')
.upload(filePath, file, {
cacheControl: '3600',
upsert: false,
})
if (error) {
throw new Error(`Upload failed: ${error.message}`)
}
// Get public URL
const { data: urlData } = supabase.storage
.from('avatars')
.getPublicUrl(filePath)
console.log('File uploaded to:', urlData.publicUrl)
return { path: data.path, url: urlData.publicUrl }
}
```
### Resumable Uploads
#### Using tus-js-client
```javascript
const tus = require('tus-js-client')
async function uploadFile(bucketName, fileName, file) {
const { data: { session } } = await supabase.auth.getSession()
return new Promise((resolve, reject) => {
var upload = new tus.Upload(file, {
endpoint: `https://${projectId}.storage.supabase.co/storage/v1/upload/resumable`,
retryDelays: [0, 3000, 5000, 10000, 20000],
headers: {
authorization: `Bearer ${session.access_token}`,
'x-upsert': 'true'
},
uploadDataDuringCreation: true,
removeFingerprintOnSuccess: true,
metadata: {
bucketName: bucketName,
objectName: fileName,
contentType: 'image/png',
cacheControl: 3600
},
chunkSize: 6 * 1024 * 1024, // Must be 6MB
onError: function (error) {
reject(error)
},
onProgress: function (bytesUploaded, bytesTotal) {
var percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2)
console.log(percentage + '%')
},
onSuccess: function () {
resolve()
}
})
return upload.findPreviousUploads().then(function (previousUploads) {
if (previousUploads.length) {
upload.resumeFromPreviousUpload(previousUploads[0])
}
upload.start()
})
})
}
```
#### Using Uppy.js
```javascript
import { Uppy, Dashboard, Tus } from 'https://releases.transloadit.com/uppy/v3.6.1/uppy.min.mjs'
const token = 'anon-key'
const projectId = 'your-project-ref'
const bucketName = 'avatars'
const supabaseUploadURL = `https://${projectId}.supabase.co/storage/v1/upload/resumable`
var uppy = new Uppy()
.use(Dashboard, {
inline: true,
target: '#drag-drop-area',
showProgressDetails: true,
})
.use(Tus, {
endpoint: supabaseUploadURL,
headers: {
authorization: `Bearer ${token}`,
},
chunkSize: 6 * 1024 * 1024,
allowedMetaFields: ['bucketName', 'objectName', 'contentType', 'cacheControl'],
})
uppy.on('file-added', (file) => {
file.meta = {
...file.meta,
bucketName: bucketName,
objectName: `folder/${file.name}`,
contentType: file.type,
}
})
uppy.on('complete', (result) => {
console.log('Upload complete!', result.successful)
})
```
### Download Files
```javascript
const { data, error } = await supabase
.storage
.from('avatars')
.download('folder/avatar1.png')
```
**With Transformations:**
```javascript
const { data, error } = await supabase
.storage
.from('avatars')
.download('folder/avatar1.png', {
transform: {
width: 100,
height: 100,
quality: 80
}
})
```
### List Files
```javascript
const { data, error } = await supabase
.storage
.from('avatars')
.list('folder', {
limit: 100,
offset: 0,
sortBy: { column: 'name', order: 'asc' }
})
```
### Update/Replace File
```javascript
const { data, error } = await supabase
.storage
.from('avatars')
.update('folder/avatar1.png', newFile, {
cacheControl: '3600',
upsert: true
})
```
### Move File
```javascript
const { data, error } = await supabase
.storage
.from('avatars')
.move('folder/avatar1.png', 'folder/avatar2.png')
```
### Copy File
```javascript
const { data, error } = await supabase
.storage
.from('avatars')
.copy('folder/avatar1.png', 'folder/avatar1-copy.png')
```
### Remove Files
```javascript
const { data, error } = await supabase
.storage
.from('avatars')
.remove(['folder/avatar1.png', 'folder/avatar2.png'])
```
---
## 4. URL Generation
### Public URLs
**For Public Buckets:**
```javascript
const { data } = supabase
.storage
.from('public-bucket')
.getPublicUrl('folder/avatar1.png')
console.log(data.publicUrl)
```
**Direct URL Format:**
```
https://[project_id].supabase.co/storage/v1/object/public/[bucket]/[asset-name]
```
**Force Download:**
```
https://[project_id].supabase.co/storage/v1/object/public/[bucket]/[asset-name]?download
https://[project_id].supabase.co/storage/v1/object/public/[bucket]/[asset-name]?download=customname.jpg
```
**With Transformations:**
```javascript
const { data } = supabase
.storage
.from('public-bucket')
.getPublicUrl('folder/avatar1.png', {
transform: {
width: 100,
height: 100,
}
})
```
### Signed URLs (Private Buckets)
**Create Single Signed URL:**
```javascript
const { data, error } = await supabase
.storage
.from('avatars')
.createSignedUrl('folder/avatar1.png', 60) // 60 seconds
```
**TypeScript Function:**
```typescript
async function createSignedUrl(filePath: string, expiresIn: number = 3600) {
const { data, error } = await supabase.storage
.from('private-files')
.createSignedUrl(filePath, expiresIn)
if (error) {
throw new Error(`Signed URL creation failed: ${error.message}`)
}
console.log('Signed URL:', data.signedUrl)
retuRelated in Image & Video
watch
IncludedWatch a video (URL or local path). Downloads with yt-dlp, extracts auto-scaled frames with ffmpeg, pulls the transcript from captions (or Whisper API fallback), and hands the result to Claude so it can answer questions about what's in the video.
physical-ai-defect-image-generation
IncludedUse when the user wants to orchestrate defect image generation, run associated setup, or handle outputs on OSMO. The Day 0 path handles cold-start with USD-to-ROI, image-edit augmentation, and AnomalyGen to create initial PCBA datasets. The Day 1 path performs inference and labeling on real images. This skill helps with first-time asset setup, creation of finetuning checkpoints, and configuring deployment. Trigger keywords: defect image generation, dig workflow, dig pipeline, defect image detection workflow, aoi pipeline, aoi anomalygen, usd2roi anomalygen, day 0 pcba, day 1 pcba, day 1 real-photo alignment, day 1 manual roi, metal surface anomaly, glass defect, anomalygen finetune, setup_pcb, setup_metal, setup_glass, setup_pretrained, dig setup, dig datasets, dig pretrained checkpoint, dig image-edit endpoint.
accelint-react-best-practices
IncludedReact performance optimization and best practices. ALWAYS use this skill when working with any React code - writing components, hooks, JSX; refactoring; optimizing re-renders, memoization, state management; reviewing for performance; fixing hydration mismatches; debugging infinite re-renders, stale closures, input focus loss, animations restarting; preventing remounting; implementing transitions, lazy initialization, effect dependencies. Even simple React tasks benefit from these patterns. Covers React 19+ (useEffectEvent, Activity, ref props). Triggers - useEffect, useState, useMemo, useCallback, memo, inline components, nested components, components inside components, re-render, performance, hydration, SSR, Next.js, useDeferredValue, combined hooks.
elevenlabs-agents
IncludedBuild conversational AI voice agents with ElevenLabs Platform using React, JavaScript, React Native, or Swift SDKs. Configure agents, tools (client/server/MCP), RAG knowledge bases, multi-voice, and Scribe real-time STT. Use when: building voice chat interfaces, implementing AI phone agents with Twilio, configuring agent workflows or tools, adding RAG knowledge bases, testing with CLI "agents as code", or troubleshooting deprecated @11labs packages, Android audio cutoff, CSP violations, dynamic variables, or WebRTC config. Keywords: ElevenLabs Agents, ElevenLabs voice agents, AI voice agents, conversational AI, @elevenlabs/react, @elevenlabs/client, @elevenlabs/react-native, @elevenlabs/elevenlabs-js, @elevenlabs/agents-cli, elevenlabs SDK, voice AI, TTS, text-to-speech, ASR, speech recognition, turn-taking model, WebRTC voice, WebSocket voice, ElevenLabs conversation, agent system prompt, agent tools, agent knowledge base, RAG voice agents, multi-voice agents, pronunciation dictionary, voice speed control, elevenlabs scribe, @11labs deprecated, Android audio cutoff, CSP violation elevenlabs, dynamic variables elevenlabs, case-sensitive tool names, webhook authentication
humanizer
IncludedHumanize AI-generated text by detecting and removing patterns typical of LLM output. Rewrites text to sound natural, specific, and human. Uses 28 pattern detectors, 560+ AI vocabulary terms across 3 tiers, and statistical analysis (burstiness, type-token ratio, readability) for comprehensive detection. Use when asked to humanize text, de-AI writing, make content sound more natural/human, review writing for AI patterns, score text for AI detection, or improve AI-generated drafts. Covers content, language, style, communication, and filler categories.
generating-mermaid-diagrams
IncludedSalesforce architecture diagrams using Mermaid with ASCII fallback. Use this skill when generating text-based diagrams for Salesforce architecture, OAuth flows, ERDs, integration sequences, or Agentforce structure. TRIGGER when: user says "diagram", "visualize", "ERD", or asks for sequence diagrams, flowcharts, class diagrams, or architecture visualizations in Mermaid. DO NOT TRIGGER when: user wants PNG/SVG image output (use generating-visual-diagrams), or asks about non-Salesforce systems.