/how-to-build-lovable

How to build Notification system with Lovable?

Step-by-step guide to build a scalable notification system with Lovable including real-time push, email, webhooks for reliability and scale

Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

Book a free No-Code consultation

How to build Notification system with Lovable?

Use a client-side, Supabase-backed notifications system: create a notifications table in Supabase (outside Lovable), store notifications there, and use the supabase-js client in the Lovable app to insert/read and subscribe to realtime changes. Configure Supabase credentials with Lovable Cloud Secrets, add the supabase-js dependency via Chat Mode edits to package.json, and add a NotificationsBell component + a Notifications page. No CLI is required inside Lovable — use Chat Mode edits, Preview, Secrets UI, and Publish. For any DB/table creation or advanced server functions you’ll open the Supabase dashboard (outside Lovable).

 

What we’re building / changing (plain English)

 

A lightweight in-app notification system that:

  • Stores notifications in a Supabase table.
  • Shows a bell icon with an unread count in the header (NotificationsBell).
  • Provides a Notifications page listing and marking-as-read notifications.
  • Receives realtime updates via supabase-js Realtime client so new notifications appear without reload.

 

Lovable-native approach

 

  • Use Chat Mode to edit files (package.json, src/lib/supabaseClient.ts, src/components/NotificationsBell.tsx, src/pages/NotificationsPage.tsx, and integrate into src/App.tsx or header component).
  • Set Supabase URL and ANON KEY in Lovable Cloud Secrets UI (no terminal).
  • Preview to test realtime behaviour in the browser inside Lovable Preview; Publish when ready.
  • If you need DB table creation or RLS policies, perform those steps in the Supabase dashboard (outside Lovable) — I’ll mark them explicitly as external steps.

 

Meta-prompts to paste into Lovable (paste each prompt as a separate Chat message)

 

Prompt A — Install dependency and create Supabase client

 

Goal: Add supabase-js dependency and a reusable client file.

  • Files to create/modify: update package.json (add dependency), create src/lib/supabaseClient.ts
  • Acceptance criteria: package.json contains an appropriate supabase-js entry; src/lib/supabaseClient.ts exports a ready-to-use client using process.env.SUPABASE_URL and process.env.SUPABASE_ANON\_KEY.
  • Secrets needed: SUPABASE_URL and SUPABASE_ANON\_KEY set in Lovable Cloud Secrets UI before Preview/Publish.
// Update package.json: add " @supabase/supabase-js": "^2.0.0" to dependencies
// Create file src/lib/supabaseClient.ts
import { createClient } from "@supabase/supabase-js";

const supabaseUrl = process.env.SUPABASE_URL!;
const supabaseAnonKey = process.env.SUPABASE_ANON_KEY!;

export const supabase = createClient(supabaseUrl, supabaseAnonKey);

 

Prompt B — Notifications UI: bell and realtime subscription

 

Goal: Add NotificationsBell component that subscribes to unread count and shows realtime updates.

  • Files to create/modify: create src/components/NotificationsBell.tsx and update your header (e.g., src/components/Header.tsx) to import and render it.
  • Acceptance criteria: Bell shows unread count; clicking opens /notifications route or dropdown; new notifications immediately increment the count without reload.
// Create src/components/NotificationsBell.tsx
import React, { useEffect, useState } from "react";
import { supabase } from "../lib/supabaseClient";

export default function NotificationsBell() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    let mounted = true;
    async function load() {
      const { data, error } = await supabase
        .from("notifications")
        .select("id")
        .eq("read", false);
      if (!error && mounted) setCount(data?.length || 0);
    }
    load();

    const subscription = supabase
      .channel("public:notifications")
      .on("postgres_changes", { event: "INSERT", schema: "public", table: "notifications" }, payload => {
        setCount(c => c + 1);
      })
      .subscribe();

    return () => {
      mounted = false;
      subscription.unsubscribe();
    };
  }, []);

  return (
    <button onClick={() => (window.location.href = "/notifications")}>
      🔔 {count > 0 ? <span>{count}</span> : null}
    </button>
  );
}

 

Prompt C — Notifications page (list, mark read, create test notification)

 

Goal: Create a page to list notifications and mark them read; include a test “Create notification” button for preview.

  • Files to create/modify: create src/pages/NotificationsPage.tsx and add a Route in src/App.tsx for /notifications (or update your routing block).
  • Acceptance criteria: Page shows notifications sorted by created\_at, can mark as read (updates DB), and the bell count updates accordingly.
// Create src/pages/NotificationsPage.tsx
import React, { useEffect, useState } from "react";
import { supabase } from "../lib/supabaseClient";

export default function NotificationsPage() {
  const [list, setList] = useState([]);

  async function load() {
    const { data } = await supabase.from("notifications").select("*").order("created_at", { ascending: false });
    setList(data || []);
  }

  async function markRead(id: string) {
    await supabase.from("notifications").update({ read: true }).eq("id", id);
    load();
  }

  async function createTest() {
    await supabase.from("notifications").insert({ title: "Test", body: "Created from Preview", read: false });
  }

  useEffect(() => { load(); }, []);

  return (
    <div>
      <h2>Notifications</h2>
      <button onClick={createTest}>Create test notification</button>
      <ul>
        {list.map(n => (
          <li key={n.id}>
            <strong>{n.title}</strong> — {n.body}
            {!n.read && <button onClick={() => markRead(n.id)}>Mark read</button>}
          </li>
        ))}
      </ul>
    </div>
  );
}

 

Supabase setup (outside Lovable)

 

  • Create table in Supabase dashboard: notifications with columns: id (uuid, primary key default gen_random_uuid()), title text, body text, read boolean default false, created_at timestamp default now(), user_id (optional).
  • Enable Realtime for the table in Supabase dashboard so client subscriptions receive INSERT/UPDATE events.
  • RLS - for a preview/demo you can allow open SELECT/INSERT/UPDATE; for production set proper RLS policies.

 

How to verify in Lovable Preview

 

  • Set SUPABASE_URL and SUPABASE_ANON\_KEY in Lovable Secrets (Cloud > Secrets) before Preview.
  • Open Preview: load the app, click Notifications in header: the page lists notifications. Click “Create test notification”: a new row appears and the bell count increases immediately.

 

How to Publish / re-publish

 

  • Use Lovable Publish button after you finish Chat Mode edits. Ensure Secrets are present in Lovable Cloud so the deployed site can connect to Supabase.
  • If you changed package.json, Publish will trigger install/build in Lovable Cloud — no terminal steps inside Lovable.

 

Common pitfalls in Lovable (and how to avoid them)

 

  • Forgot Secrets: Preview works locally in your browser only if Secrets are set; add SUPABASE_URL and SUPABASE_ANON\_KEY in Lovable Secrets UI.
  • Table missing or Realtime disabled: If realtime events don’t arrive, check Supabase table is created and Realtime is enabled (do this in Supabase dashboard outside Lovable).
  • Dependency not in package.json: If preview build errors mention missing @supabase/supabase-js, ensure package.json was updated via Chat Mode and re-preview/publish.

 

Validity bar

 

  • This flow uses only Lovable-native features: Chat Mode edits, Preview, Publish, and Secrets UI. Supabase table creation and RLS configuration are performed in the Supabase dashboard (external, no CLI required). If you need server-side logic (Edge Functions or CLI workflows), label those steps as "outside Lovable (terminal required)" and use GitHub export/sync for that code.

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Book a Free Consultation

How to handle notification provider webhooks with HMAC verification

This prompt helps an AI assistant understand your setup and guide to build the feature

AI AI Prompt

How to add idempotent notification sends with an Idempotency-Key

This prompt helps an AI assistant understand your setup and guide to build the feature

AI AI Prompt

How to add delivery-attempt tracking with exponential backoff in Lovable

This prompt helps an AI assistant understand your setup and guide to build the feature

AI AI Prompt

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Book a Free Consultation
Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

Book a free No-Code consultation

Best Practices for Building a Notification system with AI Code Generators

The short answer: build notifications as a small, observable data model (a notifications table + delivery state), let AI code generators create message drafts and templates but always run content generation inside controlled server-side code (with rate limits, caching, and content filters), use Lovable’s Secrets UI for API keys, use Supabase (or another hosted DB+realtime) for storage & real-time delivery, test and preview inside Lovable using Chat Mode edits + Preview, and export to GitHub / run DB migrations externally (or via Supabase UI) because Lovable has no terminal.

 

Design principles (quick)

 

  • Persist everything — store notification requests, generated draft, delivery attempts, and final status.
  • AI = draft author, not decision-maker — generate human-like text but validate, sanitize, and rate-limit before send.
  • Idempotency & dedupe — include request IDs so retries don’t double-send.
  • Channel abstraction — treat in-app, email, push as pluggable adapters.
  • Use Lovable-native flow — edits & patches in Chat Mode, Secrets UI for keys, Preview for UX, Publish/GitHub sync for CI/migrations.

 

Concrete pieces to implement

 

  • DB schema — notifications table with user_id, channel, body, ai_version, status, attempts, dedupe_key, metadata, created_at.
  • AI generation layer — server-side function that calls an LLM (OpenAI) to produce variants, stores drafts in DB.
  • Delivery worker — background job (Supabase Edge Function, serverless) reads queued notifications, applies filters/rate-limits, calls adapters (FCM, email provider).
  • Realtime clients — client subscribes to notifications table changes (Supabase realtime) for in-app updates.
  • Observability — logs, attempts, and a retry policy for transient failures.

 

Small, realistic examples

 

// Postgres table for notifications
CREATE TABLE notifications (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id uuid NOT NULL,
  channel text NOT NULL, // 'in_app'|'email'|'push'
  body jsonb NOT NULL, // {title, text, rendered_html}
  ai_version text,
  status text DEFAULT 'queued', // queued|sending|sent|failed
  attempts int DEFAULT 0,
  dedupe_key text,
  metadata jsonb,
  created_at timestamptz DEFAULT now()
);

 

// Server-side: generate draft with OpenAI, store via Supabase
// // uses @supabase/supabase-js and OpenAI SDK
const supabase = createClient(SUPABASE_URL, process.env.SUPABASE_SERVICE_KEY)
// // generate with OpenAI
const draft = await openai.chat.completions.create({
  model: "gpt-4o-mini",
  messages:[{role:"system",content:"Generate short notification title+body"} , {role:"user",content:"User X completed task Y"}]
})
// // insert row
await supabase.from('notifications').insert([
  { user_id, channel:'in_app', body:{title:draft.title, text:draft.text}, ai_version:'gpt-4o-mini', dedupe_key }
])

 

// Client realtime: subscribe to new notifications (supabase-js)
const supabase = createClient(SUPABASE_URL, PUBLIC_ANON_KEY)
supabase.from(`notifications:user_id=eq.${currentUserId}`)
  .on('INSERT', payload => {
    // // show in-app toast
    showToast(payload.new.body.title, payload.new.body.text)
  }).subscribe()

 

Lovable-specific workflow tips

 

  • Secrets UI — store SUPABASE_SERVICE_KEY, OPENAI_API_KEY, FCM keys in Lovable Secrets before publishing. Never paste keys into chat content.
  • Chat Mode — iterate handlers and tests with Chat Mode edits. Use file diffs/patches to implement schema and functions.
  • Preview — use Preview to simulate client subscriptions and AI-generated text. Add UI toggles for “simulate sent” vs “queued”.
  • GitHub sync — export to GitHub when you need migrations or CI. Run DB migrations via Supabase SQL editor or external CI (because Lovable has no terminal).

 

Operational cautions

 

  • Rate limits / costs — cache repeated AI outputs, batch generation when possible, and set per-user throttles.
  • Safety & personalization — sanitize PII in prompts; use content filters and user preference checks before sending.
  • Testing — include automated tests for dedupe, idempotency, and worker retries. Validate rendering for email/push variants.
  • Debugging — logs in DB rows and use Lovable Preview + exported CI logs to debug delivery; migrations run outside Lovable.

Client trust and success are our top priorities

When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.

Rapid Dev was an exceptional project management organization and the best development collaborators I've had the pleasure of working with. They do complex work on extremely fast timelines and effectively manage the testing and pre-launch process to deliver the best possible product. I'm extremely impressed with their execution ability.

CPO, Praction - Arkady Sokolov

May 2, 2023

Working with Matt was comparable to having another co-founder on the team, but without the commitment or cost. He has a strategic mindset and willing to change the scope of the project in real time based on the needs of the client. A true strategic thought partner!

Co-Founder, Arc - Donald Muir

Dec 27, 2022

Rapid Dev are 10/10, excellent communicators - the best I've ever encountered in the tech dev space. They always go the extra mile, they genuinely care, they respond quickly, they're flexible, adaptable and their enthusiasm is amazing.

Co-CEO, Grantify - Mat Westergreen-Thorne

Oct 15, 2022

Rapid Dev is an excellent developer for no-code and low-code solutions.
We’ve had great success since launching the platform in November 2023. In a few months, we’ve gained over 1,000 new active users. We’ve also secured several dozen bookings on the platform and seen about 70% new user month-over-month growth since the launch.

Co-Founder, Church Real Estate Marketplace - Emmanuel Brown

May 1, 2024 

Matt’s dedication to executing our vision and his commitment to the project deadline were impressive. 
This was such a specific project, and Matt really delivered. We worked with a really fast turnaround, and he always delivered. The site was a perfect prop for us!

Production Manager, Media Production Company - Samantha Fekete

Sep 23, 2022