Storing images in a database is generally not recommended. Doing so can quickly become expensive due to the amount of storage and processing power required. Using a cost-effective and scalable storage service like Supabase storage is better.

Below you’ll learn how to upload images to a storage bucket using the Supabase JavaScript client library and how to serve the images in a Next.js application.

Creating a Supabase Project

If you don’t already have a Next.js application ready, follow the official Next.js getting started guide to create your application.

Once you’ve done that, follow these steps to create a Supabase database:

  1. Navigate to the Supabase website and create a new account. If you already have an account, log in.
  2. From the Supabase dashboard, click the Create a new project button.
  3. Give your project a name and click the Create project button. Once Supabase creates the project, it will redirect you to the project dashboard

After creating the project, create a storage bucket.

Creating a Supabase Storage Bucket

A storage bucket allows you to store media files like images and videos. In Supabase, you can create a storage bucket on the dashboard or use the client library.

To use the dashboard, follow these steps:

  1. Go to the Supabase Storage page in the Dashboard.
  2. Click New Bucket and enter a name for the bucket. You can name it images since you’ll be storing images in it.
  3. Click Create Bucket.

Next, you will set up a Supabase client in your app to interact with the bucket.

Setting Up the Supabase Client

Begin by installing supabase-js client library via the terminal:

        npm install @supabase/supabase-js

Then create a new folder named lib at the root of your application or in the src folder if you are using it. In this folder, add a new file named supabase.js and use the following code to initialize the Supabase client.

        import { createClient } from '@supabase/supabase-js'

export const supabase = createClient('<project_url>', '<your-anon-key>')

Replace the project URL and the anon key with your own details which you can find in the Supabase project settings. You can copy the details in the .env file and reference them from there.

        SUPABASE_PROJECT_URL="your_project_url"
SUPABASE_PROJECT_ANON_KEY="your_project_anon_key"

Now, in supabase.js, you should have:

        export const supabase = createClient(
  process.env.SUPABASE_PROJECT_URL,
  process.env.SUPABASE_ANON_KEY
);

Once you’ve done that, create a form that will accept the images.

Creating a Form That Accepts Images

Inside the Next.js app folder of your application, create a subfolder named upload and add a new file named page.js. This will create a new page available at the /upload route. If you are using Next.js 12, create upload.js in the pages folder.

In the upload page, add the following code, to create the form.

        "use client";
import { useState } from "react";

export default function Page() {
  const [file, setfile] = useState([]);

  const handleSubmit = async (e) => {
    e.preventDefault();
    // upload image
  };

  const handleFileSelected = (e) => {
    setfile(e.target.files[0]);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="file" name="image" onChange={handleFileSelected} />
      <button type="submit">Upload image</button>
    </form>
  );
}

When you select a file and click the submit button, the form should call the handleSubmit function. It's in this function, that you’ll upload the image.

For large forms with multiple fields, it can be easier to use a form library like formik to handle validation and submission.

Uploading an Image File to a Supabase Storage Bucket

In the handleSubmit function, use the Supabase client to upload the image by adding the code below.

        const handleSubmit = async (e) => {
    e.preventDefault();
    const filename = `${uuidv4()}-${file.name}`;

    const { data, error } = await supabase.storage
      .from("images")
      .upload(filename, file, {
        cacheControl: "3600",
        upsert: false,
      });

    const filepath = data.path;
    // save filepath in database
  };

In this function, you are creating a unique file name by concatenating the file name and a UUID generated by the uuid npm package. This is recommended to avoid overwriting files that share the same name.

You will need to install the uuid package via npm so copy and run the command below in the terminal.

        npm install uuid

Then, at the top of the upload page, import version 4 of uuid.

        import { v4 as uuidv4 } from "uuid";
    

If you don't want to use the uuid package, there are some other methods you can use to generate unique ids.

Next, use the supabase client to upload the file to the storage bucket called “images”. Remember to import the supabase client at the top of your file.

        import { supabase } from "@/lib/supabase";

Note that you are passing the path to the image and the image itself. This path works the same as it does in the file system. For instance, if you were saving the image to a folder in the bucket called public, you would specify the path as “/public/filename”.

Supabase will return an object containing the data and error object. The data object contains the URL to the image you just uploaded.

For this upload function to work, you must create an access policy that allows your application to insert and read data in a Supabase storage bucket by following these steps:

  1. On your project dashboard, click on the Storage tab on the left-hand sidebar.
  2. Select your storage bucket and click on the Access tab.
  3. Under Bucket policies, click the New policy button.
  4. Select the For full customization option to create a policy from scratch.
  5. In the Add policy dialog, enter a name for your policy (e.g. "Allow Insert and Read").
  6. Select INSERT and SELECT permissions from the Allowed operations dropdown menu.
  7. Click the Review button to review the policies.
  8. Click the Save button to add the policy.

You should now be able to upload images without raising an access error.

Serving the Uploaded Images in Your Application

To serve the image on your site, you need a public URL, which you can retrieve in two different ways.

You can use the Supabase client like this:

        const filepath = "path_to_file_in_buckey"

const { data } = supabase
  .storage
  .from('images')
  .getPublicUrl(`${filepath}`)

Or you can manually concatenate the bucket URL with the file path:

        const filepath = `${bucket_url}/${filepath}`

Use whichever method you prefer. Once you have the file path, you can use it in the Next.js image component like this:

        <Image src={filepath} alt="" width={200} height={200}/>

This code will display the image on your site.

Creating Robust Applications With Supabase

Using this code, you can accept a file from a user through a form and upload it to Supabase storage. You can then retrieve that image and serve it on your site.

Besides storing images, Supabase has other functionality. You can create a PostgreSQL database, write edge functions, and set up authentication for your users.