Skip to main content
RapidDev - Software Development Agency
App Featuresai-features17 min read

How to Add Product Recommendations to Your App (Copy-Paste Prompts Included)

Product recommendations need three pieces: a behaviour tracker that records views and purchases, a recommendation engine (Supabase pgvector + OpenAI text-embedding-3-small for semantic similarity, or rule-based SQL JOINs for simpler catalogues), and a card grid UI that renders results. With Lovable or V0 you can ship a working recommendations widget in 4-8 hours. Running costs start at $0 on Supabase free tier and reach $25-30/mo at 1,000 active users.

4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members

Feature spec

Intermediate

Category

ai-features

Build with AI

4-8 hours with Lovable or V0

Custom build

1-2 weeks custom dev

Running cost

$0/mo up to ~200 users; $25-30/mo at 1,000 users

Works on

Web

Everything it takes to ship Product Recommendations — parts, prompts, and real costs.

TL;DR

Product recommendations need three pieces: a behaviour tracker that records views and purchases, a recommendation engine (Supabase pgvector + OpenAI text-embedding-3-small for semantic similarity, or rule-based SQL JOINs for simpler catalogues), and a card grid UI that renders results. With Lovable or V0 you can ship a working recommendations widget in 4-8 hours. Running costs start at $0 on Supabase free tier and reach $25-30/mo at 1,000 active users.

What a Product Recommendations Feature Actually Is

Product recommendations surface items a user is likely to want based on what they and similar users have already viewed, clicked, or bought. The 'you may also like' widget is so common that users now expect it on any product-bearing page — its absence signals an incomplete experience. The engineering reality is two sub-problems: collecting events (views, clicks, purchases) and turning those events into a ranked list. For most early-stage apps, Supabase pgvector with OpenAI embeddings solves both — each item gets an embedding vector, and nearest-neighbour search finds semantically similar items weighted by event frequency. The product decisions that matter are cold-start behaviour for new users, how many results to show, and where on the page the widget lives.

What users consider table stakes in 2026

  • Recommendations appear without page reload — rendered inline as the product page loads, not after a visible delay
  • Personalisation visibly improves after 2-3 interactions — a first-time user sees category trending, a returning user sees personalised picks
  • A 'Why recommended' signal (e.g. 'Because you viewed X' or 'Popular in this category') increases trust and click-through rate
  • Users can dismiss or hide individual recommendations; dismissed items stay hidden for the session
  • Card grid is mobile-responsive — horizontal scroll on narrow viewports, 2- or 3-column grid on desktop
  • Empty state handled gracefully when a new user has no events yet — show bestsellers or trending items, never a blank space

Anatomy of the Feature

Six components. The recommendation engine and data model are where AI tools need precise prompting — everything else generates reliably.

Layers:UIDataBackend

Recommendation engine

Backend

Supabase Edge Function (Deno) that queries pgvector nearest-neighbours on the item_embeddings table or runs SQL frequency analysis on user_item_events and returns a ranked list of item IDs. OpenAI text-embedding-3-small converts item titles and descriptions into 1536-dimension vectors.

Note: For catalogues under 5,000 items, SQL JOIN-based collaborative filtering (most co-viewed items) is simpler and faster than pgvector. Switch to vector similarity when catalogue grows or cross-category recommendations are needed.

User behaviour tracker

Data

Records every view, click, add_to_cart, and purchase as a row in user_item_events with user_id, item_id, event_type, and timestamp. This table feeds both the collaborative filtering model and the analytics dashboard.

Note: Debounce impression events — a scroll-past should not count as a view. Fire the insert only when an item is visible for at least 2 seconds using IntersectionObserver.

Recommendation API

Backend

Supabase Edge Function endpoint that accepts user_id and item_id (current page context), runs the recommendation logic, and returns up to 12 ranked item objects. Results are cached in recommendations_cache for 1 hour to avoid repeated pgvector queries.

Note: Return the full item object (title, image_url, price, slug) from this function — avoid a second round-trip from the frontend.

Product card grid UI

UI

React grid using shadcn/ui Card components in a responsive CSS grid (2 columns mobile, 3-4 desktop). Each card shows image, title, price, and an inline 'Add to cart' CTA. Skeleton loading states via shadcn/ui Skeleton render while the API responds.

Note: Skeleton cards prevent layout shift. Show exactly as many skeletons as the expected result count (6 is common) so the page doesn't jump.

Personalisation store

Data

Two Supabase tables: user_item_events stores raw behavioural signals; item_embeddings stores the pgvector vectors for each catalogue item. RLS on user_item_events ensures each user's event history is private — no cross-user data leakage.

Note: item_embeddings is not user-specific and can be read publicly; RLS only needed on user_item_events.

Analytics hook

Backend

Impression and click events fired to Supabase from the recommendation widget itself — separate from product page events. Tracks recommendation_click with item_id, position_in_list, and source_item_id so you can measure which recommendations convert.

Note: Debounce inserts and batch them — fire in groups of 5 or every 10 seconds to avoid hammering the database with single-row inserts.

The data model

Three tables cover the full recommendations data model. Run this in the Supabase SQL editor (Dashboard → SQL editor → New query):

schema.sql
1-- Enable pgvector extension (one-time, in Supabase Dashboard Database Extensions)
2-- Search for "vector" and toggle on before running the SQL below.
3
4create table public.items (
5 id uuid primary key default gen_random_uuid(),
6 title text not null,
7 description text,
8 category text,
9 image_url text,
10 price numeric(10,2),
11 slug text unique not null,
12 created_at timestamptz not null default now()
13);
14
15create table public.item_embeddings (
16 item_id uuid primary key references public.items(id) on delete cascade,
17 embedding vector(1536) not null
18);
19
20create index on public.item_embeddings
21 using hnsw (embedding vector_cosine_ops);
22
23create table public.user_item_events (
24 id uuid primary key default gen_random_uuid(),
25 user_id uuid references auth.users(id) on delete cascade not null,
26 item_id uuid references public.items(id) on delete cascade not null,
27 event_type text not null check (event_type in ('view','click','add_to_cart','purchase')),
28 created_at timestamptz not null default now()
29);
30
31alter table public.user_item_events enable row level security;
32
33create policy "Users can insert own events"
34 on public.user_item_events for insert
35 with check (auth.uid() = user_id);
36
37create policy "Users can read own events"
38 on public.user_item_events for select
39 using (auth.uid() = user_id);
40
41create index user_item_events_user_idx
42 on public.user_item_events (user_id, created_at desc);
43
44create table public.recommendations_cache (
45 user_id uuid references auth.users(id) on delete cascade not null,
46 item_ids jsonb not null,
47 generated_at timestamptz not null default now(),
48 primary key (user_id)
49);
50
51alter table public.recommendations_cache enable row level security;
52
53create policy "Users can read own cache"
54 on public.recommendations_cache for select
55 using (auth.uid() = user_id);
56
57create policy "Service role manages cache"
58 on public.recommendations_cache for all
59 using (true)
60 with check (true);

Heads up: The HNSW index on item_embeddings is critical for query performance — without it, pgvector does a full sequential scan and times out above ~1,000 items. The recommendations_cache policy grants full access to the service role (used by Edge Functions) while restricting direct reads to the owning user.

Build it — pick your path

Each tool fits this feature differently. Switch tabs to compare fit, time, the exact steps, and a copy-paste prompt.

Hand-built by developersFit for this feature:

Full custom development is the right path at 10K+ users where basic similarity breaks down, or when recommendation accuracy is a core product differentiator requiring ML model training and A/B testing.

Step by step

  1. 1Implement gradient boosting or neural collaborative filtering (LightFM, PyTorch) trained on full event history with explicit purchase signals weighted higher
  2. 2Run simultaneous A/B tests on different recommendation algorithms with statistical significance tracking via PostHog or Statsig
  3. 3Migrate from pgvector to a dedicated vector database (Pinecone, Weaviate) when catalogue exceeds 100K items to maintain sub-100ms query latency
  4. 4Build a multi-channel recommendation model that unifies web, email, and push signals into one ranking

Where this path bites

  • Requires ML engineering expertise that is overkill for most apps under 50K monthly active users
  • Training and serving ML models adds significant infrastructure cost and complexity

Third-party services you'll need

The core recommendation engine uses only Supabase and OpenAI. No third-party recommendation SaaS is required for an initial implementation:

ServiceWhat it doesFree tierPaid from
OpenAI Embeddings APIConverts item titles and descriptions into 1536-dimension vectors for semantic similarity search via pgvectorNo free tier; pay-as-you-go from first call$0.02 per 1M tokens (approx, text-embedding-3-small, 2026)
Supabase ProPostgreSQL database with pgvector extension enabled; hosts all tables, Edge Functions, and authFree tier (2 projects, 500MB DB) — pgvector available but not guaranteed at scale$25/mo (Pro, 8GB DB, pgvector fully supported)

Swipe the table sideways to see pricing.

What it costs to run

Drag through the tiers to see how your monthly bill scales with users — no surprises later.

Estimated monthly running cost

$0/mo

Supabase free tier covers usage easily. Embedding generation for 100 items costs under $0.01. Recommendation queries negligible at this user count.

Estimates use verified 2026 pricing and assume typical usage per user. Your real bill depends on activity, storage, and third-party plans.

What breaks when AI tools build this

The failures people actually hit on their first build — the symptom, why it happens, and the exact fix.

All users see identical recommendations

Symptom: When the RLS policy is missing from user_item_events, every user's events merge into a shared pool. The recommendation engine sees the combined history of all users as if it belonged to the current user, so everyone gets the same globally popular items regardless of their personal behaviour.

Fix: Add `USING (auth.uid() = user_id)` and `WITH CHECK (auth.uid() = user_id)` policies to user_item_events. Test by logging in as two different users, generating distinct event histories, and verifying the recommendations differ.

pgvector queries time out after ~500ms

Symptom: Without an index, pgvector performs a sequential scan of the entire item_embeddings table for every nearest-neighbour query. At 1,000+ items this regularly exceeds 500ms; at 5,000+ items it times out Edge Function execution entirely.

Fix: Run `CREATE INDEX ON item_embeddings USING hnsw (embedding vector_cosine_ops);` in the Supabase SQL editor. HNSW indexes reduce query time to single-digit milliseconds even at 100K vectors. This must be created after populating the table with data.

New users always see the same 6 items (cold start)

Symptom: AI tools generate recommendation logic that assumes event history exists. When a new user with 0-2 events hits the recommendations widget, the pgvector query returns 0 results or falls back incorrectly, leaving the widget empty or throwing an unhandled error.

Fix: Add an explicit conditional in the Edge Function: if `event_count < 3`, skip pgvector and instead return the 6 most-viewed items from the last 7 days ordered by view count. Your prompt must state this cold-start behaviour or AI tools will omit it.

Duplicate view events fire on every scroll

Symptom: When the event tracker is implemented with a basic React useEffect tied to element visibility, it re-fires every time the component re-renders — which on a scrollable product page can be dozens of times per session. This floods user_item_events with duplicate view rows that corrupt the collaborative filtering signal.

Fix: Implement the impression tracker with IntersectionObserver and a `hasTracked` ref (a React ref object, not state) that is set to true on first fire and prevents all subsequent calls in the same session. Do not use state for this — state changes trigger re-renders which re-trigger the observer.

Best practices

1

Always implement cold-start fallback — show trending or bestseller items for users with fewer than 3 events rather than an empty widget

2

Cache recommendation results in recommendations_cache with a 1-hour TTL; the pgvector query is expensive and results change slowly

3

Track recommendation clicks separately from regular product clicks — you need position-in-list data to know which slots in the widget actually convert

4

Debounce view events with IntersectionObserver and a hasTracked ref; never track a view on React re-render, only on first genuine scroll-into-view

5

Exclude already-purchased items from recommendations — showing a product the user already bought is a trust signal failure

6

Generate item embeddings asynchronously when items are created or updated, not synchronously in the product save API route

7

Limit the recommendation widget to 6 items — more than 6 hurts click-through rate and makes the page feel algorithmic rather than curated

8

Add a 'Why recommended' label (e.g. 'Popular in Kitchen') even if it is rule-based rather than ML-driven; it increases conversion by 15-25% in typical A/B tests

When You Need Custom Development

Lovable and V0 comfortably cover personalised recommendations for catalogues under 50K items and user bases under 10K. Certain requirements push beyond what AI tools can deliver:

  • Recommendation accuracy must improve with explicit ML model training — gradient boosting or neural collaborative filtering trained on full purchase history
  • You need to A/B test different recommendation algorithms simultaneously and measure statistical significance of revenue lift
  • Your catalogue exceeds 100K items — pgvector nearest-neighbour at this scale requires a dedicated vector database like Pinecone or Weaviate with sharding
  • Multi-channel recommendations are needed — the same model must power web widgets, email campaigns, and push notifications from a single signal source

RapidDev builds these features for production

Real auth, database, and payments — fixed price, delivered in weeks. You own 100% of the code.

Get a fixed-price quote

Frequently asked questions

How do product recommendations work when I have no user data yet?

Start with content-based filtering: embed your item descriptions with OpenAI and find the nearest neighbours to whatever product a visitor is viewing. This works for the first user on day one — no behavioural data needed. Collaborative filtering (based on what similar users bought) layers on top once you have 50-100 users with purchase history.

Can I add recommendations to an existing Supabase database?

Yes. The only additions required are: enable the pgvector extension, add the item_embeddings table and HNSW index, add the user_item_events table with RLS, and deploy the Edge Function. Your existing items table does not need schema changes — the embedding table references it by item_id.

What is the difference between collaborative filtering and content-based recommendations?

Content-based filtering looks at what an item is (title, description, category) and recommends similar items. Collaborative filtering looks at what similar users did (users who bought X also bought Y). Content-based works from day one with no user data; collaborative filtering gets more accurate as you collect purchase and view events. Most production systems blend both.

How do I prevent recommendations from showing items the user already purchased?

In the Edge Function, query the user's purchase events first and collect the purchased item IDs, then add a NOT IN exclusion to the pgvector query. Your prompt should explicitly state 'exclude items the user has already purchased' — AI tools omit this logic unless instructed.

Do I need a paid OpenAI account to build recommendations?

Yes — the Embeddings API has no free tier. However, costs are tiny: generating embeddings for a 1,000-item catalogue costs under $0.05 total with text-embedding-3-small. You only pay again when items are added or updated. The ongoing per-recommendation call only hits OpenAI when no cached result exists.

How many user events are needed before recommendations become personalised?

Three to five events is a practical minimum — enough to establish a preference signal above random noise. Below that threshold, serve trending items as fallback. Genuine personalisation (recommendations that feel accurate to the user) typically requires 10-20 events per user including at least one purchase or add-to-cart.

Can recommendations work for a small catalogue of 50 products?

Yes, and at 50 items you can skip pgvector entirely. Generate all pairwise embeddings once and store cosine similarity scores in a static JSON file or a simple cross-reference table. This approach has zero query latency and no pgvector complexity — practical until your catalogue exceeds 500-1,000 items.

How do I track which recommendations actually converted to sales?

Fire a recommendation_click event when a user clicks a recommended card, storing the item_id, position in the widget, and the source_item_id (the product page they were on). Then join these clicks against purchase events to calculate your recommendation conversion rate. Build this tracking into the card's onClick handler before you go live — retrofitting it after is much harder.

RapidDev

Need this feature production-ready?

RapidDev builds product recommendations into real apps — auth, database, payments — at $13K–$25K.

Book a free consultation

30-min call. No commitment.

Want this built for you?

We ship production apps at a fixed price — $13K–$25K, 6–10 weeks, source code yours. You've seen what it takes; we do it every week.

Get a fixed-price quote

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.