# Supabase Storage

> Apply when handling file uploads, downloads, and storage management in Supabase.

- Skill: `majiayu000/supabase-storage` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add majiayu000/supabase-storage`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/supabase-storage/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/supabase-storage

---


## When to Use

Apply when handling file uploads, downloads, and storage management in Supabase.

## Patterns

### Pattern 1: Upload File
```typescript
// Source: https://supabase.com/docs/reference/javascript/storage-from-upload
async function uploadFile(file: File, userId: string) {
  const fileExt = file.name.split('.').pop();
  const fileName = `${userId}/${Date.now()}.${fileExt}`;

  const { data, error } = await supabase.storage
    .from('avatars') // bucket name
    .upload(fileName, file, {
      cacheControl: '3600',
      upsert: false, // false = error if exists, true = overwrite
    });

  if (error) throw error;
  return data.path;
}
```

### Pattern 2: Get Public URL
```typescript
// Source: https://supabase.com/docs/reference/javascript/storage-from-getpublicurl
// For public buckets
const { data } = supabase.storage
  .from('avatars')
  .getPublicUrl('user123/avatar.png');

const publicUrl = data.publicUrl;

// With transformations
const { data } = supabase.storage
  .from('avatars')
  .getPublicUrl('user123/avatar.png', {
    transform: { width: 200, height: 200, resize: 'cover' },
  });
```

### Pattern 3: Signed URL (Private Buckets)
```typescript
// Source: https://supabase.com/docs/reference/javascript/storage-from-createsignedurl
const { data, error } = await supabase.storage
  .from('private-docs')
  .createSignedUrl('user123/document.pdf', 3600); // 1 hour expiry

if (data) {
  window.open(data.signedUrl, '_blank');
}
```

### Pattern 4: Delete File
```typescript
// Source: https://supabase.com/docs/reference/javascript/storage-from-remove
const { error } = await supabase.storage
  .from('avatars')
  .remove(['user123/old-avatar.png']);

// Delete multiple
const { error } = await supabase.storage
  .from('avatars')
  .remove(['file1.png', 'file2.png', 'file3.png']);
```

### Pattern 5: React Upload Component
```typescript
// Source: https://supabase.com/docs/guides/storage
function AvatarUpload({ userId, onUpload }: Props) {
  const [uploading, setUploading] = useState(false);

  const handleUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file) return;

    setUploading(true);
    try {
      const path = await uploadFile(file, userId);
      onUpload(path);
    } catch (error) {
      alert('Upload failed');
    } finally {
      setUploading(false);
    }
  };

  return (
    <input
      type="file"
      accept="image/*"
      onChange={handleUpload}
      disabled={uploading}
    />
  );
}
```

## Anti-Patterns

- **No file validation** - Check size/type before upload
- **Predictable paths** - Use UUIDs or timestamps in paths
- **No error handling** - Handle upload failures gracefully
- **Missing RLS on bucket** - Configure storage policies

## Verification Checklist

- [ ] File size limit enforced client-side
- [ ] File type validation (accept attribute + server)
- [ ] Unique file paths (prevent overwrites)
- [ ] Storage policies configured for bucket
- [ ] Loading state during upload

