# How to Add Price Alerts to Your App (Copy-Paste Prompts Included)

- Tool: App Features
- Last updated: July 2026

## TL;DR

Price alerts need four pieces: a target-price form on the product detail page, a price monitoring cron job (Supabase Edge Function or Vercel Cron running every 15–60 minutes), an alert evaluation SQL query that fires only once per threshold cross, and a notification sender (FCM for mobile push, web-push for browser, Resend for email). With Lovable or V0 you can ship this in 4–6 hours for $0–5/month at small scale — FCM push delivery is free at any volume and Resend covers early email volume on the free tier.

## What Price Alerts Actually Are

Price alerts let shoppers set a target price on a product and get notified the moment the price drops to or below that target — without checking back manually. For e-commerce and marketplace apps, this is a conversion engine: users who set a price alert have already decided to buy, so the notification converts at significantly higher rates than typical marketing emails. The mechanics are a monitoring cron job that compares the current product price against every active alert for that product, plus a deduplication system that prevents the same alert from firing every time the cron runs while the price stays low.

## Anatomy of the Feature

Seven components across four layers. Lovable and V0 generate the UI and the basic cron setup well. The deduplication logic and the FCM mobile push setup are where first builds fail.

- **Set Alert Button** (ui): A 'Notify me when price drops to $X' button on the product detail page. Opens a modal (shadcn/ui Dialog on web, Flutter AlertDialog on mobile) with a target price number input pre-filled at 10% below the current price. Saves to the price_alerts table on submit.
- **Price Alert Form** (ui): Target price number input with currency symbol and locale-aware formatting (USD, EUR, GBP). Optional channel toggle (email or push, or both). Validated with React Hook Form + Zod on web or flutter_form_builder on Flutter. Inserts into price_alerts table with expires_at set to NOW() + 90 days.
- **Price Monitoring Job** (backend): Supabase Edge Function or Vercel Cron running every 15–60 minutes. Queries the products table for prices that have changed since the last run (compare against a cached previous_price column or the price_history table). For each changed product, passes the new price to the Alert Trigger Engine for evaluation.
- **Alert Trigger Engine** (backend): SQL function: SELECT pa.* FROM price_alerts pa JOIN products p ON pa.product_id = p.id WHERE p.current_price <= pa.target_price AND pa.is_active = true AND pa.last_fired_at IS NULL AND pa.expires_at > NOW(). Sets is_active = false after firing so the alert doesn't re-trigger while the price stays low. User can reset the alert from the dashboard if they want to be notified again.
- **Push Notification** (service): Firebase Cloud Messaging (FCM) for mobile push via the firebase_messaging Flutter package on native apps. Web-push with VAPID keys for browser push on web. Notification payload includes product name, current price, target price, and a deep link to the product page.
- **Email Notification** (service): Resend transactional email with product image, a price comparison table (was $X, now $Y, your target was $Z), and a prominent 'Buy Now' CTA button. Sent from a Supabase Edge Function using RESEND_API_KEY from Supabase Secrets.
- **Alert Management Screen** (ui): List of all active price alerts with product image thumbnail, current price, target price, a visual progress bar showing how close the current price is to the target, expiry date, and edit/delete/pause controls. Loaded from price_alerts with a JOIN to products for current prices.

## Data model

Two tables cover alerts and price history. Run this in the Supabase SQL editor. The price_alerts table references your existing products table — adjust the foreign key if your products table has a different name.

```sql
create table public.price_alerts (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users(id) on delete cascade not null,
  product_id uuid references public.products(id) on delete cascade not null,
  target_price numeric not null check (target_price > 0),
  last_fired_at timestamptz,
  is_active bool not null default true,
  expires_at timestamptz not null default (now() + interval '90 days'),
  created_at timestamptz not null default now()
);

create table public.price_history (
  id uuid primary key default gen_random_uuid(),
  product_id uuid references public.products(id) on delete cascade not null,
  price numeric not null,
  recorded_at timestamptz not null default now()
);

alter table public.price_alerts enable row level security;
alter table public.price_history enable row level security;

create policy "Users manage own price alerts"
  on public.price_alerts for all
  using (auth.uid() = user_id)
  with check (auth.uid() = user_id);

create policy "Authenticated users view price history"
  on public.price_history for select
  using (auth.role() = 'authenticated');

create policy "Service role inserts price history"
  on public.price_history for insert
  with check (auth.role() = 'service_role');

create index price_alerts_product_active_idx
  on public.price_alerts (product_id, is_active)
  where is_active = true;

create index price_alerts_user_idx
  on public.price_alerts (user_id, created_at desc);

create index price_alerts_expiry_idx
  on public.price_alerts (expires_at)
  where is_active = true;

create index price_history_product_time_idx
  on public.price_history (product_id, recorded_at desc);
```

The partial index on active alerts keyed by product_id makes the evaluation query fast — for a given product price change, finding all active alerts for that product is a single index scan rather than a full table scan. The expiry index supports a nightly cleanup job that auto-deactivates alerts past their expires_at date.

## Build paths

### Lovable — fit 4/10, 4–6 hours

Best all-round path: Lovable's Supabase integration handles auth, the price_alerts table, Edge Functions for monitoring and evaluation, and Resend for email. pg_cron schedules the price check automatically. Strong fit for e-commerce products already built in Lovable.

1. Create a new Lovable project (or open an existing one with your products table) and connect Lovable Cloud so Supabase auth and Edge Functions are provisioned
2. Paste the prompt below and let Agent Mode scaffold the alert form, alert management screen, and Edge Functions
3. In Lovable Cloud tab → Secrets, add RESEND_API_KEY, VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, and FCM_SERVER_KEY if building for mobile push
4. Enable pg_cron in Supabase (requires Pro plan, $25/mo) and schedule the price check Edge Function to run every 30 minutes
5. Publish to your custom domain and test alert delivery on the published URL — pg_cron notifications do not fire in the Lovable preview environment
6. Test on a real mobile device for push delivery; FCM cannot be tested in the Lovable preview iframe

Starter prompt:

```
Build a price alert feature for an e-commerce app using Supabase and Resend.

Database: Create price_alerts table (id uuid primary key default gen_random_uuid(), user_id uuid references auth.users, product_id uuid references products(id) on delete cascade, target_price numeric check target_price > 0, last_fired_at timestamptz, is_active bool default true, expires_at timestamptz default now() + interval '90 days', created_at timestamptz default now()). Create price_history table (id, product_id, price numeric, recorded_at timestamptz). Enable RLS: authenticated users manage own price_alerts rows; price_history readable by authenticated users.

UI:
1. On the product detail page, add a 'Notify me when price drops' button that opens a modal with a target price input pre-filled at 10% below the current price, an optional email notification toggle, and a save button that inserts into price_alerts
2. Alert management page at /my-alerts showing all active alerts with product image, current price, target price, progress bar, expiry date, and edit/pause/delete controls; paginate to 20 per page with load-more

Edge Functions:
1. check-prices: queries products table for any items whose current_price changed in the last hour; for each changed product, evaluates all active price_alerts WHERE current_price <= target_price AND is_active = true AND last_fired_at IS NULL AND expires_at > NOW(); for each match: set is_active = false, send Resend email (RESEND_API_KEY from Secrets) with product name, current price, target price, and buy-now link; insert into price_history
2. push-subscribe: saves push subscription (endpoint, p256dh, auth) to push_subscriptions table
3. send-push: sends web push notification via web-push npm package using VAPID keys from Secrets

Edge cases:
- If product goes out of stock (is_available = false on products table), pause the alert (set is_active = false) but do NOT delete it; show 'Out of stock' badge on the alert card
- Validate that target_price > 0 before saving
- Multiple alerts from the same user on the same product are allowed — track each independently
- Show an in-app permission request explaining why notifications are useful before triggering browser push permission dialog
```

Limitations:

- Mobile push via FCM cannot be triggered from a Lovable (Vite/React) web app without a native wrapper — web push via VAPID is the mobile browser alternative, which requires Add to Home Screen on iOS
- pg_cron requires Supabase Pro ($25/mo) — on the free tier, use an external free cron service (cron-job.org) to call the Edge Function URL on a schedule
- Alert delivery cannot be tested in the Lovable preview environment — always test on the published URL

### V0 — fit 4/10, 4–5 hours

V0 generates polished Next.js product pages with clean alert UI and Vercel Cron for price monitoring. Best when your product catalog is already in a Next.js app or when you want a professional price comparison display.

1. Prompt V0 with the spec below to generate the alert button, modal, management page, and API routes
2. In the V0 Vars panel, add RESEND_API_KEY, NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, and SUPABASE_SERVICE_KEY
3. Run the SQL schema above in the Supabase SQL editor
4. Add vercel.json with the cron config: { "crons": [{ "path": "/api/check-prices", "schedule": "*/30 * * * *" }] } — V0 generates the route but not the cron config file
5. Publish to Vercel and verify the cron job appears in the Vercel dashboard under the Crons tab

Starter prompt:

```
Build a price alert feature for a Next.js e-commerce app using Supabase (service key from SUPABASE_SERVICE_KEY env var) and Resend (RESEND_API_KEY).

Database tables already exist (or create them): price_alerts (id uuid, user_id uuid, product_id uuid, target_price numeric, last_fired_at timestamptz, is_active bool default true, expires_at timestamptz default now() + interval '90 days', created_at timestamptz). RLS: users manage own rows.

Build:
1. PriceAlertButton component: 'Notify me when price drops to $X' button on product detail page; opens a shadcn/ui Dialog with a target price input (pre-filled at current_price * 0.90), Zod validation (must be > 0 and < current_price), email toggle, push notification opt-in; saves to price_alerts via Supabase client
2. /my-alerts page: list of active alerts with product image (from products table), current price vs target price, progress bar showing % to target, expiry date, edit target price in place, pause toggle, delete button; paginate 20 per page
3. /api/check-prices route (for Vercel Cron): uses SUPABASE_SERVICE_KEY to query products WHERE updated_at > NOW() - INTERVAL '35 minutes'; for each changed product, SELECT pa.* FROM price_alerts pa WHERE pa.product_id = $1 AND pa.is_active = true AND pa.last_fired_at IS NULL AND p.current_price <= pa.target_price AND pa.expires_at > NOW(); for each match: UPDATE price_alerts SET is_active = false WHERE id = $alert_id; send Resend email with product name, current price, target price, buy-now URL; guard: if product has is_available = false, set alert is_active = false without sending notification; guard: skip if new_price <= 0 or is NULL
4. /api/push-subscribe route: saves push subscription to push_subscriptions table

Locale-aware currency formatting: use Intl.NumberFormat for all price displays. Handle the case where the same user has multiple alerts on the same product — allow it and track each independently.
```

Limitations:

- Mobile push via FCM requires native setup that V0 won't auto-configure — the V0 path is best suited for web-only price alerts via email and browser push
- V0 generates the /api/check-prices route but not the vercel.json crons config — add it manually before deploying
- The reminder deduplication logic (setting is_active = false after firing) must be described explicitly in the prompt; V0 may generate a last_fired_at cooldown approach instead, which allows re-firing

### Flutterflow — fit 3/10, 5–7 hours

Use FlutterFlow when your marketplace is mobile-first and you need native FCM push with direct-to-product deep links on iOS and Android. Firebase handles push delivery and Cloud Functions evaluate price changes.

1. Connect your FlutterFlow project to Firebase (Authentication + Firestore + Cloud Functions); enable the Blaze pay-as-you-go plan for Cloud Functions
2. Create Firestore collections: price_alerts (userId, productId, targetPrice, isActive, expiresAt, lastFiredAt) and price_history (productId, price, recordedAt)
3. Build the Set Alert bottom sheet in FlutterFlow with a NumberField for target price pre-filled at current price minus 10%, and save to Firestore on submit
4. In the Firebase Functions editor (JavaScript), write the price monitoring function triggered on Firestore product price_update: evaluate all active alerts for the changed product, send FCM push for matches via admin.messaging()
5. Build the My Alerts list page in FlutterFlow with a Firestore Query on price_alerts filtered by userId; add edit, pause, and delete actions

Limitations:

- Price monitoring logic must be written in Cloud Functions (JavaScript) — FlutterFlow cannot visually design backend evaluation logic
- iOS requires an APNs Auth Key uploaded in Firebase Console before FCM push works on real devices; FlutterFlow doesn't prompt for this during setup
- Firestore costs more at scale than PostgreSQL for high-write price history logging — budget accordingly beyond 10K users

### Custom — fit 5/10, 1–2 weeks

Custom development for price alert infrastructure that must react to price changes in sub-minute latency via Supabase Realtime triggers, support multi-currency with live FX conversion, or deliver via SMS as a mandatory channel.

1. Supabase Realtime trigger on the products table price column: when current_price changes, an Edge Function is triggered immediately via a Postgres trigger + net.http_post() without waiting for the next cron run
2. Multi-currency alert engine: store target_price in the user's preferred currency, convert to product base currency at evaluation time using an FX rate table updated hourly from the European Central Bank API
3. SMS delivery via Twilio for mandatory channel: send a short message with product name, price, and a shortened buy link; handle Twilio delivery receipts to confirm message received
4. Price history chart on the alert management screen using Recharts — line chart showing price_history for the last 90 days with the target_price as a horizontal threshold line

Limitations:

- Supabase Realtime trigger on a high-write products table (frequent price updates) may introduce notification latency if the trigger function queue backs up
- Twilio SMS adds per-message cost ($0.0079/message in US) plus a phone number rental fee (~$1/mo) — significant at scale

## Gotchas

- **Same price alert fires every time the cron runs while price stays low** — The alert evaluation query checks whether current_price <= target_price, which remains true as long as the price stays at or below the target. Without a definitive 'fired' state, every cron run sends a new notification. Using only a last_fired_at cooldown (e.g. don't re-fire within 1 hour) still lets the alert fire repeatedly every hour for as long as the price is low. Fix: Set is_active = false immediately after firing, inside the same database transaction as the notification send. This is a permanent state: the alert won't fire again until the user explicitly resets it from the dashboard. Add a 'Reset alert' button on the alert card that sets is_active = true, so users can opt back in to future notifications on the same product.
- **FCM push works on Android but not on iOS in FlutterFlow** — iOS FCM push requires an APNs (Apple Push Notification service) Auth Key to be uploaded in the Firebase Console, in addition to the google-services.json configuration. FlutterFlow connects to Firebase and configures Android FCM automatically, but doesn't prompt the developer to upload the APNs key. Without it, iOS devices register for FCM but never receive messages — the registration appears to succeed, making the issue invisible until testing on a real iPhone. Fix: In the Apple Developer Portal, go to Certificates, Identifiers & Profiles → Keys → create a new key with the Apple Push Notifications Service (APNs) capability. Download the .p8 file. In Firebase Console → Project Settings → Cloud Messaging → iOS app configuration, upload the .p8 file with the Key ID and Team ID. Regenerate and re-upload this key if it expires or if you rotate your Apple Developer membership.
- **Alert management screen is slow for users with 50+ active alerts** — The default Supabase query for a user's price_alerts table fetches all rows at once with a JOIN to the products table for current prices. A user who has set alerts on 100 products receives a 100-row JSON payload on every page load, causing a noticeable render delay on mobile and a flash of loading state on every navigation to the page. Fix: Add .range(from, to) pagination to the Supabase query — start with 20 alerts per page and a 'Load more' button. Use SWR or React Query with a stale-while-revalidate strategy to avoid blocking navigation. On mobile Flutter, use Supabase's stream() with a range parameter and a ListView.builder that renders lazily — only the visible rows are rendered at any time.
- **Alert fires when product price temporarily errors and returns $0** — If the product price feed has a bug, a data import error, or a momentary API outage, the current_price column might be set to 0 or NULL. Any alert with a target_price > 0 satisfies the condition current_price <= target_price when price is 0, so every active alert fires simultaneously — which looks like a mass notification bug to users. Fix: Add a guard in the alert evaluation function: skip any product where new_price <= 0 OR new_price IS NULL before comparing against target prices. Log anomalous price values to a price_anomalies table for review. Optionally, add a plausibility check: if the new price is more than 90% lower than the previous recorded price in price_history within the last hour, treat it as suspect and skip alert evaluation for that product until the price stabilizes.

## Best practices

- Pre-fill the target price input at 10% below the current price — users who see a number already entered convert to saving alerts at much higher rates than users who see an empty input
- Set alerts to is_active = false (not just cooldown) after firing — price drop conditions can persist for days, and hourly re-notification is a fast way to get unsubscribes
- Add a 'Reset alert' button so users can opt back in after an alert fires without deleting and recreating the alert with the same parameters
- Guard against $0 and NULL prices in your evaluation query — a data error can trigger thousands of simultaneous false alerts
- Auto-expire alerts after 90 days (default) with an editable expires_at field — stale alerts are noise on the management screen and waste evaluation cycles
- Include the product image in email notifications — price drop emails with product visuals have measurably higher click-through rates than text-only variants
- Paginate the alert management screen at 20 items — users with large wishlists will set many alerts and the page must stay fast

## Frequently asked questions

### How quickly will I be notified after a price drops?

With a polling-based approach (Lovable or V0), the notification fires within the cron interval — typically 15–60 minutes after the price changes. For most e-commerce apps, 30-minute latency is acceptable. If your products have flash sales where prices change and sell out within minutes, you need a Supabase Realtime trigger on the price column that fires the evaluation immediately on every price update, which requires custom development.

### Can I set a price alert without creating an account?

Not with this architecture — price alerts are stored in the database linked to a user_id, which requires authentication to persist across sessions. You could implement a guest alert using a browser cookie and email address only, but the alert would be lost if the user clears cookies. The standard pattern is to require a lightweight account creation (email/password or Google OAuth) before setting an alert, since most users who want to track prices are willing to create a free account.

### Will the alert fire for a small price drop of just $1?

Yes — the alert fires whenever current_price <= target_price, regardless of the size of the drop. If you want to prevent alerts for trivially small drops (e.g., only notify on drops of 5% or more), add a minimum_discount_pct column to the price_alerts table and add a condition in the evaluation query: AND (original_price - current_price) / original_price >= minimum_discount_pct. Store the original_price at alert creation time as a reference point.

### What if the product goes out of stock before the price drops?

When the product becomes unavailable (is_available = false on the products table), the monitoring job should pause the alert (set is_active = false) but not delete it. Show an 'Out of stock' badge on the alert card in the management screen. When the product comes back in stock, automatically resume the alert (set is_active = true) — this creates a better experience than forcing users to recreate their alert when stock returns.

### Can I set alerts on multiple products at once?

That depends on the UI you build. The standard pattern is one alert per product — users set an alert on each product detail page individually. You could add a 'bulk alert' feature on a wishlist or cart page that sets a 10% discount alert on all items in one action. Each alert is stored as a separate row in price_alerts, so the backend handles multiples natively.

### Do alerts expire automatically?

Yes, in the schema above, expires_at defaults to 90 days from creation. A nightly cleanup query (or pg_cron job) sets is_active = false for all rows where expires_at < NOW(). The expiry date is visible on the alert management screen, and users can edit it to extend it. 90 days covers most seasonal shopping cycles while keeping the active alert table clean.

### How do I cancel all my price alerts at once?

Add a 'Clear all alerts' button on the alert management page that runs UPDATE price_alerts SET is_active = false WHERE user_id = auth.uid(). For users who want to permanently delete rather than just pause, add a 'Delete all' button that runs DELETE FROM price_alerts WHERE user_id = auth.uid(). Give the delete action a confirmation dialog — this action is not reversible.

### Does this work on mobile apps too?

Yes — the Supabase backend works identically for both web and mobile. On mobile Flutter apps, replace browser push (VAPID/web-push) with Firebase Cloud Messaging (firebase_messaging package), which delivers push notifications to iOS and Android even when the app is fully closed. The price_alerts database schema, the cron monitoring job, and the email delivery via Resend work identically on both platforms.

---

Source: https://www.rapidevelopers.com/app-features/price-alerts
© RapidDev — https://www.rapidevelopers.com/app-features/price-alerts
