SupaLaunch is a SaaS boilerplate built using Supabase and Next.js. It includes authentication, Stripe payments, Postgres database, 20+ TailwindCSS themes, emails, OpenAI API streaming, file storage and more.
Want to manage files in your Next.js app securely and efficiently? Supabase Storage is a powerful tool that helps you handle images, videos, and documents with ease. It offers built-in authentication, real-time updates, and scalable architecture, making it a great choice for SaaS projects and startups.
This guide walks you through setting up Supabase Storage, managing files, and securing access with Row-Level Security (RLS). Dive in to learn how to optimize file handling for your Next.js application.
https://www.youtube-nocookie.com/embed/87JAdYPC2n0
Getting Supabase Storage up and running with Next.js involves a few clear, secure steps.
Before diving in, make sure you have the following ready:
To handle file storage in your Next.js app, you’ll first need to create a storage bucket in Supabase. Here’s how:
CREATE POLICY "Allow uploads" ON storage.objects FOR
INSERT WITH CHECK (
bucket_id = 'documents' AND auth.role() = 'anon'
);
Next, add your Supabase credentials to the .env.local
file in your project’s root directory:
NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
After setting up the environment variables, install the required package:
npm install @supabase/auth-helpers-nextjs
Then, create a Supabase client instance in your project:
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
const supabase = createClientComponentClient();
Once this setup is complete, you’re all set to start integrating Supabase Storage into your Next.js application [1][2].
With your environment ready, let’s walk through how to integrate Supabase Storage into your Next.js application.
First, install the Supabase JavaScript library:
npm install @supabase/supabase-js
This package streamlines file storage operations and makes client authentication easier [1].
Create a utility file, utils/supabase.ts
, to initialize your Supabase client:
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
);
export default supabase;
This setup will serve as the foundation for interacting with Supabase Storage [1][2].
Uploading files is a core feature of any storage system. Here’s how you can implement it in your Next.js app:
import supabase from '../utils/supabase';
const FileUploadComponent = () => {
const handleFileUpload = async (event) => {
const file = event.target.files[0];
const { data, error } = await supabase.storage
.from('documents')
.upload(`${Date.now()}-${file.name}`, file);
if (error) {
console.error('Upload failed:', error.message);
} else {
console.log('Uploaded:', data);
}
};
return (
<input
type="file"
onChange={handleFileUpload}
accept="image/*,application/pdf"
/>
);
};
This example includes:
accept
attribute [1]To further refine your file uploads, consider adding these checks:
| Type | Purpose | Example Check |
|-----------|-------------------------|-----------------------------------------|
| File Size | Block oversized uploads | file.size <= 5 * 1024 * 1024
(5MB) |
| File Type | Allow specific formats | Match file.type
to allowed MIME types |
| File Name | Ensure safe file names | Remove special characters and spaces |
With file uploads covered, you’re ready to move on to managing and securing your stored files.
These methods are particularly helpful for SaaS applications where secure and efficient file handling is a priority.
Here's a practical way to manage file uploads while addressing potential errors:
const handleFileUpload = async (file) => {
try {
// Check file size before uploading
if (file.size > 5 * 1024 * 1024) {
throw new Error('File size exceeds 5MB limit');
}
const { data, error } = await supabase.storage
.from('documents')
.upload(`${Date.now()}-${file.name}`, file, {
cacheControl: '3600', // Sets browser cache duration in seconds
upsert: false // Prevents overwriting existing files
});
if (error) throw error;
return { success: true, data };
} catch (error) {
console.error("Error uploading file:", error.message);
return { success: false, error: error.message };
}
};
Common upload issues and how to address them:
| Error Type | Solution |
| --- | --- |
| Size Limit | Validate size before uploading |
| Invalid Type | Restrict uploads to specific MIME types |
| Auth Error | Ensure the user is logged in |
| Duplicate Files | Use upsert: true
to overwrite |
Here’s an example of generating and using a temporary file URL for secure access:
const FileDisplay = ({ filePath }) => {
const [fileUrl, setFileUrl] = useState(null);
useEffect(() => {
const getFileUrl = async () => {
// Generate a temporary URL for secure access
const { data, error } = await supabase.storage
.from('documents')
.createSignedUrl(filePath, 3600);
if (data) setFileUrl(data.signedUrl);
};
getFileUrl();
}, [filePath]);
return fileUrl ? (
<img src={fileUrl} alt="Stored file" />
) : (
<p>Loading...</p>
);
};
To control access to files, use Row Level Security (RLS) policies:
-- Allow users to access only their own files
CREATE POLICY "Allow individual access"
ON storage.objects FOR SELECT
TO authenticated
USING (auth.uid() = owner_id);
For even more control, implement role-based restrictions in your application:
const downloadFile = async (fileId) => {
const { data: { role } } = await supabase.auth.getUser();
// Ensure the user has the required role
if (role !== 'premium_user') {
throw new Error('Access denied: Premium subscription required');
}
const { data, error } = await supabase.storage
.from('documents')
.download(fileId);
if (error) throw error;
return data;
};
Improving file storage can boost the performance of your Next.js app. Use the <Image>
component from Next.js for better image delivery, paired with Supabase Storage's caching options for smoother performance.
To manage files effectively, try this caching strategy:
const uploadWithCache = async (file) => {
const { data, error } = await supabase.storage
.from('images')
.upload(`optimized/${file.name}`, file, {
cacheControl: '31536000',
contentType: file.type,
upsert: true
});
};
Once storage is optimized, you can take it a step further by adding real-time file updates to your app.
Supabase subscriptions make it easy to monitor storage changes as they happen:
const StorageMonitor = () => {
useEffect(() => {
const subscription = supabase
.channel('storage-changes')
.on(
'postgres_changes',
{
event: '*',
schema: 'storage',
table: 'objects'
},
(payload) => {
updateUIWithNewFile(payload.new);
}
)
.subscribe();
return () => {
subscription.unsubscribe();
};
}, []);
};
When handling multiple files, process them efficiently in chunks:
const processFileQueue = async (files) => {
const chunkSize = 2;
for (let i = 0; i < files.length; i += chunkSize) {
const chunk = files.slice(i, i + chunkSize);
await Promise.all(
chunk.map(file => uploadWithCache(file))
);
}
};
For a quicker setup, you can use SupaLaunch, which provides ready-made solutions for real-time file management.
Setting up Supabase Storage involves a few essential steps: creating a storage bucket, configuring environment variables, and integrating the Supabase client for secure file management [1]. Adding Row-Level Security (RLS) policies ensures that only approved users can access or interact with stored files, making your application ready for production [1].
Once these steps are in place, Supabase Storage becomes a strong option for managing files in SaaS applications.
To get the most out of Supabase Storage, consider focusing on a few key areas to enhance your application. These include refining file validation processes, improving storage performance, and using real-time features for dynamic updates - all while maintaining strong security practices.
Here are some practical tips:
Following these steps will help you create a reliable, scalable file management system for your Next.js application.