Build An Image CMS with Supabase and Lovable

Kirill InozKirill Inoz
7 min read

In one of my previous blog posts, I demonstrated how to build a MidJourney alternative using Supabase and Hugging Face in a few minutes. This is great for personal use, but what are you going to do with these generated images? And what if you want to share it with your friends or customers? I decided to take things further from that article and create a more comprehensive application — a CMS-like tool for managing and generating AI-powered as well as normal images. That’s what ImageVault accomplishes. It allows users to upload, store, create and share these images with a user-friendly interface.

This project was powered by a combination of tools: Lovable (GPT Engineer) allowed me to generate and refine code, Supabase gave this project the backend functionalities it needed to store and manage the images. Where Lovable struggled, I utilized Visual Studio Code and my software engineering skills to fix the issues. Spoiler, I didn’t have to use VSCode that much. I will show you the journey from the first prompt to a functioning application, as well as the challenges and lessons learned when working with such a GPT Engineer like Lovable.

Laying the Groundwork

The Importance of a Solid Plan

When experimenting with Lovable, I’ve found that creating a detailed initial prompt is crucial. Lovable excels when it comes to implementing large and cohesive features, but often struggles with more fine-tuned, piecemeal fixes. Therefore, I would advise planning out your first prompt by for example utilizing ChatGPT to give your prompt a structure and fill any gaps it might have. Once I had my solid plan with key functionalities and some good to have additions for ImageVault, I uploaded it to Lovable. It took some time to process, but the result was mind-blowing, we had a working skeleton application in seconds.

The First Prompt and Loading Screen in Lovable

Getting Started with ImageVault

Lovable provided me with some components such as an upload are for images, basic authentication pages, simple dashboard to display uploaded images. After seeing the initial results, I exported the code to GitHub and decided to review it in VSCode. This allowed me to become more familiar with the generated code, so I would be able to make any adjustments later on and have an overview of what minor or major changes Lovable might make down the line.

An interesting, unexpected touch was Lovable actually giving the tool the name ImageVault itself. I thought the name perfectly captures the concept we are after with this image storage platform, so why not to keep this sense of identity from the start, right.

The Application Skeleton Lovable Created

Integrating Supabase

Connecting Supabase

As the next step, it was a good idea to integrate Supabase to handle backend operation like authentication and image storage. I started by creating a Supabase project and setting up a test user in the Authentication tab. Then, I used Lovable’s built-in “Connect to a project” functionality to seamlessly link Supabase to the application.

Connect Supabase Project

Adding Authentication

With Supabase connected, I added authentication functionality. Even though the process of creating authentication in Supabase is documented in tons of articles and videos, Lovable made it even easier than easy. I simply typed “Add authentication“. First Lovable suggests to you any SQL changes it wants to do, giving you the final decision on any major changes. Secondly it updated the sign-in and sign-up pages with suitable functionalities. After testing the test user’s credentials, I found out that it didn’t miss a single thing in this setup. It worked perfectly. Just wow.

Sign-In Page in ImageVault

Fixing the Image Feed

While the upload functionality worked, the images didn’t display properly in the feed. Initially, I asked Lovable to fetch and display the images, but it struggled to render them correctly. I was about fix this issue manually as I thought it would take less time, but I decided to stick to Lovable and instead try to make more precise prompts. I started giving it code snippets, entering my ideas and thoughts on how to fix it and guiding it towards the solution. After some back and forth we solved the issue and the images were visible, the links were public and copyible and the images can be deleted as well. This iterative process proved the improtance of small and focused prompts when working with Lovable later on.

Enhancing the UI

While testing the UI, I noticed some minor inconsistencies in the padding and margins, especially after adding the input and button for generating images and comparing the image grid to the rest of the UI. It was nothing major what couldn’t be fixed inside of VSCode, I decided not do it for now to show you what you get when working with Lovable in the final demo video later on in the article.

Adding Hugging Face Integration

With the feed functioning, I brought back the image generation feautre from my earlier project, integrating Hugging Face into the app. I had difficulties integrating Edge Functions, after one year my blog post seemed to be a bit outdated, especially when it comes to the new Supabase UI and some functionalities. After struggling for a while, I decided to integrate the generation into the frontend. Basically we generate the image and upload it to the Supabase Storage.

const [prompt, setPrompt] = useState("");
const [isGenerating, setIsGenerating] = useState(false);
const { toast } = useToast();
const queryClient = useQueryClient();

const handleGenerate = async () => {
    if (!prompt.trim()) return;

    try {
      setIsGenerating(true);

      // Get the current user
      const {
        data: { user },
      } = await supabase.auth.getUser();
      if (!user) {
        toast({
          title: "Error",
          description: "You must be logged in to generate images",
          variant: "destructive",
        });
        return;
      }

      // Initialize HuggingFace client
      const hf = new HfInference(import.meta.env.VITE_HUGGINGFACE_ACCESS_TOKEN);

      // Generate image using HuggingFace
      const image = await hf.textToImage({
        inputs: prompt,
        model: "stabilityai/stable-diffusion-2",
        parameters: {
          negative_prompt: "blurry",
        },
      });

      // Convert blob to File for upload
      const imageFile = new File([image], "generated-image.png", {
        type: "image/png",
      });

      // Create a unique filename
      const fileName = `${user.id}/${crypto.randomUUID()}.png`;

      // Upload to Supabase Storage
      const { error: uploadError } = await supabase.storage
        .from("images")
        .upload(fileName, imageFile, {
          contentType: "image/png",
          upsert: false,
        });

      if (uploadError) throw uploadError;

      // Create database entry
      const { error: imageError } = await supabase.from("images").insert({
        storage_path: fileName,
        title: prompt,
        user_id: user.id,
        is_ai_gen: true, // Set the is_ai_gen field to true
      });

      if (imageError) throw imageError;

      // Invalidate and refetch images query
      await queryClient.invalidateQueries({ queryKey: ["images"] });

      toast({
        title: "Success",
        description: "Image generated successfully",
      });
    } catch (error: any) {
      console.error("Generation error:", error);
      toast({
        title: "Error",
        description: error.message || "Failed to generate image",
        variant: "destructive",
      });
    } finally {
      setIsGenerating(false);
    }
  };

Lovable was able to adapt existing tables and components to the new requirements. Even though it has some great features like Edit or Revert, I noticed that I didn’t use them that much. Especially Revert was a bit disappointing because it only reverted the code but the Supabase changes to the tables stayed the same which caused me some headache in the project.

Edit and Revert Functionalities

Finally there were some finishing touches, like creating a landing page, creating a full-view modal and adding a robot icon to AI-generated images, so they can be differentiated from uploaded images. Here I made the mistake of putting all of these things into one prompt thinking these were some smaller changes, so what could go wrong. That’s where the revert function came to be handy. In my attempts of fixing everything what went wrong in image generation and route handling after entering this prompt, I only broke the implementation even more. Luckily after reverting and doing some manuals changes in VSCode I was able to get it back working for the demo. But before the demo…

Landing Page Lovable Created

Challenges and Lessons Learned

Version Restore in Lovable

As already mentioned previously it’s a very very needed functionality in such a GPT Engineer, I don’t know how I could’ve escaped those debugging loops without manually going into the code. One things, it would be nice to have an option to revert Supabase changes as well.

Prompting Tips

Through this process, I learned a few valuable tips for working with Lovable:

  • Start with a detailed, well-structued inital prompt

  • For subsequent changes, focus on one feature at a time to avoid breaking existing functionality.

Demo

I present to you ImageVault - a comprehensive CMS for uploading, storing and generating AI-powered images. It includes features like user authentication, secure storage, real-time updates, shareable links, all wrapped in an average user-friendly interface. All-in-all I liked working with Lovable and I can see myself using it for quick demos or MVPs for my future projects. I’m very eager to see how it’s going to evolve and if it could replace software engineers in the future, but until then it still has some issues to overcome.


Thanks for reading! ❤️ This article is part of the Content Storm which celebrates Supabase Launch Week 13. If you want to be the first one to see my next article, follow me on Hashnode, BlueSky and X!

2
Subscribe to my newsletter

Read articles from Kirill Inoz directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Kirill Inoz
Kirill Inoz

I'm a self-taught web developer. I occasionally post summaries of my projects and what I learned from them. Thanks for tuning in! ❤