Sign In

We're the bridge between marketing ambition and financial wisdom. Born from the frustration of seeing e-commerce companies chase growth at any cost, we built a platform that turns complex customer data into simple profit signals.

Ready to turn your marketing budget into pure rocket fuel? 🚀

Drop us a line at hello@headw.ai and let's explore how we can boost your ROI. (We promise the demo will be worth your time!)

© Copyright 2026 Headwai. All Rights Reserved.

    Legal
    • Privacy Policy

    File Uploads

    Handle file uploads with Supabase Storage.

    Note: This is mock/placeholder content for demonstration purposes.

    Enable users to upload and manage files using Supabase Storage.

    Setup

    Create Storage Bucket

    -- Create a public bucket for avatars
    INSERT INTO storage.buckets (id, name, public)
    VALUES ('avatars', 'avatars', true);
    
    -- Create a private bucket for documents
    INSERT INTO storage.buckets (id, name, public)
    VALUES ('documents', 'documents', false);
    

    Set Storage Policies

    -- Allow users to upload their own avatars
    CREATE POLICY "Users can upload their own avatar"
    ON storage.objects FOR INSERT
    WITH CHECK (
      bucket_id = 'avatars' AND
      auth.uid()::text = (storage.foldername(name))[1]
    );
    
    -- Allow users to view their own avatars
    CREATE POLICY "Users can view their own avatar"
    ON storage.objects FOR SELECT
    USING (
      bucket_id = 'avatars' AND
      auth.uid()::text = (storage.foldername(name))[1]
    );
    
    -- Allow users to delete their own avatars
    CREATE POLICY "Users can delete their own avatar"
    ON storage.objects FOR DELETE
    USING (
      bucket_id = 'avatars' AND
      auth.uid()::text = (storage.foldername(name))[1]
    );
    

    Upload Component

    Basic File Upload

    'use client';
    
    import { useState } from 'react';
    import { uploadFileAction } from '../_lib/actions';
    
    export function FileUpload() {
      const [uploading, setUploading] = useState(false);
      const [file, setFile] = useState<File | null>(null);
    
      const handleUpload = async () => {
        if (!file) return;
    
        setUploading(true);
    
        const formData = new FormData();
        formData.append('file', file);
    
        const result = await uploadFileAction(formData);
    
        if (result.success) {
          toast.success('File uploaded successfully');
        }
    
        setUploading(false);
      };
    
      return (
        <div>
          <input
            type="file"
            onChange={(e) => setFile(e.files?.[0] || null)}
            accept="image/*"
          />
          <button
            onClick={handleUpload}
            disabled={!file || uploading}
          >
            {uploading ? 'Uploading...' : 'Upload'}
          </button>
        </div>
      );
    }
    

    Server Action

    'use server';
    
    import { enhanceAction } from '@kit/next/actions';
    import { getSupabaseServerClient } from '@kit/supabase/server-client';
    
    export const uploadFileAction = enhanceAction(
      async (formData: FormData, user) => {
        const file = formData.get('file') as File;
    
        if (!file) {
          throw new Error('No file provided');
        }
    
        const client = getSupabaseServerClient();
        const fileExt = file.name.split('.').pop();
        const fileName = `${user.id}/${Date.now()}.${fileExt}`;
    
        const { data, error } = await client.storage
          .from('avatars')
          .upload(fileName, file, {
            cacheControl: '3600',
            upsert: false,
          });
    
        if (error) throw error;
    
        // Get public URL
        const { data: { publicUrl } } = client.storage
          .from('avatars')
          .getPublicUrl(fileName);
    
        return {
          success: true,
          url: publicUrl,
          path: data.path,
        };
      },
      { auth: true }
    );
    

    Drag and Drop Upload

    'use client';
    
    import { useCallback } from 'react';
    import { useDropzone } from 'react-dropzone';
    
    export function DragDropUpload() {
      const onDrop = useCallback(async (acceptedFiles: File[]) => {
        for (const file of acceptedFiles) {
          const formData = new FormData();
          formData.append('file', file);
          await uploadFileAction(formData);
        }
      }, []);
    
      const { getRootProps, getInputProps, isDragActive } = useDropzone({
        onDrop,
        accept: {
          'image/*': ['.png', '.jpg', '.jpeg', '.gif'],
        },
        maxSize: 5 * 1024 * 1024, // 5MB
      });
    
      return (
        <div
          {...getRootProps()}
          className={cn(
            'border-2 border-dashed rounded-lg p-8 text-center cursor-pointer',
            isDragActive && 'border-primary bg-primary/10'
          )}
        >
          <input {...getInputProps()} />
          {isDragActive ? (
            <p>Drop files here...</p>
          ) : (
            <p>Drag and drop files here, or click to select</p>
          )}
        </div>
      );
    }
    

    File Validation

    Client-Side Validation

    function validateFile(file: File) {
      const maxSize = 5 * 1024 * 1024; // 5MB
      const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
    
      if (file.size > maxSize) {
        throw new Error('File size must be less than 5MB');
      }
    
      if (!allowedTypes.includes(file.type)) {
        throw new Error('File type must be JPEG, PNG, or GIF');
      }
    
      return true;
    }
    

    Server-Side Validation

    export const uploadFileAction = enhanceAction(
      async (formData: FormData, user) => {
        const file = formData.get('file') as File;
    
        // Validate file size
        if (file.size > 5 * 1024 * 1024) {
          throw new Error('File too large');
        }
    
        // Validate file type
        const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
        if (!allowedTypes.includes(file.type)) {
          throw new Error('Invalid file type');
        }
    
        // Validate dimensions for images
        if (file.type.startsWith('image/')) {
          const dimensions = await getImageDimensions(file);
          if (dimensions.width > 4000 || dimensions.height > 4000) {
            throw new Error('Image dimensions too large');
          }
        }
    
        // Continue with upload...
      },
      { auth: true }
    );
    

    Image Optimization

    Resize on Upload

    import sharp from 'sharp';
    
    export const uploadAvatarAction = enhanceAction(
      async (formData: FormData, user) => {
        const file = formData.get('file') as File;
        const buffer = Buffer.from(await file.arrayBuffer());
    
        // Resize image
        const resized = await sharp(buffer)
          .resize(200, 200, {
            fit: 'cover',
            position: 'center',
          })
          .jpeg({ quality: 90 })
          .toBuffer();
    
        const client = getSupabaseServerClient();
        const fileName = `${user.id}/avatar.jpg`;
    
        const { error } = await client.storage
          .from('avatars')
          .upload(fileName, resized, {
            contentType: 'image/jpeg',
            upsert: true,
          });
    
        if (error) throw error;
    
        return { success: true };
      },
      { auth: true }
    );
    

    Progress Tracking

    'use client';
    
    import { useState } from 'react';
    
    export function UploadWithProgress() {
      const [progress, setProgress] = useState(0);
    
      const handleUpload = async (file: File) => {
        const client = getSupabaseBrowserClient();
    
        const { error } = await client.storage
          .from('documents')
          .upload(`uploads/${file.name}`, file, {
            onUploadProgress: (progressEvent) => {
              const percent = (progressEvent.loaded / progressEvent.total) * 100;
              setProgress(Math.round(percent));
            },
          });
    
        if (error) throw error;
      };
    
      return (
        <div>
          <input type="file" onChange={(e) => handleUpload(e.target.files![0])} />
          {progress > 0 && (
            <div className="w-full bg-gray-200 rounded-full h-2">
              <div
                className="bg-primary h-2 rounded-full transition-all"
                style={{ width: `${progress}%` }}
              />
            </div>
          )}
        </div>
      );
    }
    

    Downloading Files

    Get Public URL

    const { data } = client.storage
      .from('avatars')
      .getPublicUrl('user-id/avatar.jpg');
    
    console.log(data.publicUrl);
    

    Download Private File

    const { data, error } = await client.storage
      .from('documents')
      .download('private-file.pdf');
    
    if (data) {
      const url = URL.createObjectURL(data);
      const a = document.createElement('a');
      a.href = url;
      a.download = 'file.pdf';
      a.click();
    }
    

    Generate Signed URL

    const { data, error } = await client.storage
      .from('documents')
      .createSignedUrl('private-file.pdf', 3600); // 1 hour
    
    console.log(data.signedUrl);
    

    Deleting Files

    export const deleteFileAction = enhanceAction(
      async (data, user) => {
        const client = getSupabaseServerClient();
    
        const { error } = await client.storage
          .from('avatars')
          .remove([data.path]);
    
        if (error) throw error;
    
        return { success: true };
      },
      {
        schema: z.object({
          path: z.string(),
        }),
        auth: true,
      }
    );
    

    Best Practices

    1. Validate on both sides - Client and server
    2. Limit file sizes - Prevent abuse
    3. Sanitize filenames - Remove special characters
    4. Use unique names - Prevent collisions
    5. Optimize images - Resize before upload
    6. Set storage policies - Control access
    7. Monitor usage - Track storage costs
    8. Clean up unused files - Regular maintenance
    9. Use CDN - For public files
    10. Implement virus scanning - For user uploads