Skip to main content
RapidDev - Software Development Agency

How to Build Personalization system with V0

Build a behavioral content personalization engine with V0 using Next.js, Supabase with pgvector, and A/B testing. You'll create a rule-based system that segments users by behavior, serves tailored content variants, and tracks experiment results — all in about 2-4 hours from your browser.

What you'll build

  • Behavioral event tracking pipeline that ingests user actions via an API route
  • Rule engine that evaluates personalization rules against user segments in real time
  • Admin dashboard with shadcn/ui Tabs, Switch, and Slider for managing rules and priorities
  • A/B testing framework with impression and conversion tracking per variant
  • User segment computation using Supabase database functions and pgvector embeddings
  • Edge-optimized evaluation endpoint for sub-50ms personalization responses
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced11 min read2-4 hoursV0 Premium or higherApril 2026RapidDev Engineering Team
TL;DR

Build a behavioral content personalization engine with V0 using Next.js, Supabase with pgvector, and A/B testing. You'll create a rule-based system that segments users by behavior, serves tailored content variants, and tracks experiment results — all in about 2-4 hours from your browser.

What you're building

Personalization is the difference between a product that feels generic and one that feels made for each user. Whether you are recommending content, adjusting UI layouts, or running A/B tests on feature variations, a personalization engine lets you serve the right experience to the right person at the right time.

V0 accelerates this build by generating the Next.js code for event tracking endpoints, rule evaluation logic, and admin dashboards from natural-language prompts. Use the Connect panel to provision Supabase with pgvector enabled, then queue up prompts for each subsystem while V0 generates them in sequence.

The architecture uses Next.js App Router with an Edge-optimized API route for rule evaluation, Server Actions for rule management, Supabase for storing events and segments, pgvector for embedding-based user similarity, and Recharts for A/B test result visualization.

Final result

A complete personalization platform with behavioral event tracking, segment-based rule evaluation, A/B testing with statistical significance indicators, and an admin dashboard for managing personalization rules.

Tech stack

V0AI Code Generator
Next.jsFull-Stack Framework
Tailwind CSSStyling
shadcn/uiComponent Library
SupabaseDatabase
pgvectorAI Embeddings
RechartsData Visualization

Prerequisites

  • A V0 account (Premium or higher recommended for prompt queuing)
  • A Supabase project with pgvector extension enabled (free tier works)
  • Basic understanding of user segmentation concepts
  • A product or website where you want to personalize content

Build steps

1

Set up the project and Supabase schema with pgvector

Open V0 and create a new project. Use the Connect panel to add Supabase, then prompt V0 to generate the full schema including pgvector for user embeddings. This foundation handles events, rules, segments, and A/B tests.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Build a personalization engine. Create a Supabase schema with these tables:
3// 1. user_profiles: id (uuid PK), user_id (uuid FK unique), preferences (jsonb), segments (text[]), created_at (timestamptz), updated_at (timestamptz)
4// 2. events: id (uuid PK), user_id (uuid FK), event_type (text), event_data (jsonb), page_url (text), created_at (timestamptz)
5// 3. personalization_rules: id (uuid PK), name (text), segment_criteria (jsonb), content_variant (jsonb), priority (integer), is_active (boolean), created_at (timestamptz)
6// 4. ab_tests: id (uuid PK), rule_id (uuid FK), variant_key (text), impressions (integer default 0), conversions (integer default 0), created_at (timestamptz)
7// Enable pgvector extension and add: user_embeddings (id uuid PK, user_id uuid FK, embedding vector(1536), updated_at timestamptz)
8// Add RLS policies so only authenticated users can read their own profiles.
9// Generate SQL migration and TypeScript types.

Pro tip: Use V0's prompt queuing — queue the schema prompt first, then immediately queue the event tracker and rule engine prompts while the first one generates.

Expected result: Supabase is connected via the Connect panel with all five tables created, pgvector extension enabled, and TypeScript types generated.

2

Build the behavioral event tracking API

Create an API route that ingests user behavioral events. This endpoint is called from your frontend on every meaningful action — page views, clicks, purchases — and stores them in the events table for later segment computation.

app/api/events/track/route.ts
1import { NextRequest, NextResponse } from 'next/server'
2import { createClient } from '@supabase/supabase-js'
3
4const supabase = createClient(
5 process.env.SUPABASE_URL!,
6 process.env.SUPABASE_SERVICE_ROLE_KEY!
7)
8
9export async function POST(req: NextRequest) {
10 const { user_id, event_type, event_data, page_url } = await req.json()
11
12 if (!user_id || !event_type) {
13 return NextResponse.json(
14 { error: 'user_id and event_type are required' },
15 { status: 400 }
16 )
17 }
18
19 const { error } = await supabase.from('events').insert({
20 user_id,
21 event_type,
22 event_data: event_data ?? {},
23 page_url: page_url ?? null,
24 })
25
26 if (error) {
27 return NextResponse.json({ error: error.message }, { status: 500 })
28 }
29
30 return NextResponse.json({ success: true })
31}

Expected result: POST requests to /api/events/track insert behavioral events into Supabase. Each event includes the user ID, event type, custom data, and page URL.

3

Create the personalization rule evaluation endpoint

Build the core engine that evaluates personalization rules against a user's segments. This endpoint checks which rules match the current user and returns the highest-priority content variant. Use Edge Runtime for minimal latency.

app/api/personalization/evaluate/route.ts
1import { NextRequest, NextResponse } from 'next/server'
2import { createClient } from '@supabase/supabase-js'
3
4export const runtime = 'edge'
5
6export async function GET(req: NextRequest) {
7 const supabase = createClient(
8 process.env.SUPABASE_URL!,
9 process.env.SUPABASE_SERVICE_ROLE_KEY!
10 )
11
12 const userId = req.nextUrl.searchParams.get('user_id')
13 if (!userId) {
14 return NextResponse.json({ error: 'user_id required' }, { status: 400 })
15 }
16
17 const { data: profile } = await supabase
18 .from('user_profiles')
19 .select('segments, preferences')
20 .eq('user_id', userId)
21 .single()
22
23 if (!profile) {
24 return NextResponse.json({ variant: null, fallback: true })
25 }
26
27 const { data: rules } = await supabase
28 .from('personalization_rules')
29 .select('id, name, segment_criteria, content_variant, priority')
30 .eq('is_active', true)
31 .order('priority', { ascending: false })
32
33 const matchedRule = rules?.find((rule) => {
34 const criteria = rule.segment_criteria as { segments?: string[] }
35 return criteria.segments?.some((s: string) => profile.segments?.includes(s))
36 })
37
38 if (matchedRule) {
39 await supabase.rpc('increment_impressions', { rule_id: matchedRule.id })
40 }
41
42 return NextResponse.json({
43 variant: matchedRule?.content_variant ?? null,
44 rule_name: matchedRule?.name ?? null,
45 fallback: !matchedRule,
46 })
47}

Pro tip: Edge Runtime runs your evaluation endpoint on Vercel's global edge network, cutting latency to under 50ms for most users worldwide.

Expected result: GET /api/personalization/evaluate?user_id=xxx returns the highest-priority matching content variant for that user, or a fallback flag if no rules match.

4

Build the rule management admin dashboard

Prompt V0 to generate an admin interface where you create and manage personalization rules. Each rule targets specific user segments and defines a content variant to serve. Include priority ordering and active/inactive toggles.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Build a personalization rule management dashboard at app/dashboard/personalization/page.tsx.
3// Requirements:
4// - Fetch all personalization_rules and display in a shadcn/ui Table
5// - Each row shows: name, target segments (as Badges), priority (number), is_active (Switch toggle), created_at
6// - Add a "Create Rule" Button that opens a Sheet from the right side
7// - Sheet contains: Input for rule name, Select for target segments (multi-select), JSON editor for content_variant, Slider for priority (1-100)
8// - Switch toggles should immediately update is_active via Server Action
9// - Add Tabs at the top: "Rules" and "A/B Tests"
10// - A/B Tests tab shows a Table with variant_key, impressions, conversions, conversion rate, and a Badge showing statistical significance (green if >95% confidence)
11// - Use Recharts BarChart to visualize conversion rates across variants
12// - Use Server Components for data fetching, 'use client' only for interactive elements

Expected result: A dashboard with Tabs for Rules and A/B Tests. Rules are displayed in a Table with Switch toggles. A Sheet opens for creating new rules with segment targeting and priority.

5

Implement segment computation with database functions

Create a Supabase database function that recomputes user segments based on their event history. This runs periodically via pg_cron and updates the user_profiles.segments array, which the evaluation endpoint reads for fast rule matching.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Create a Supabase SQL migration for segment computation:
3// 1. A database function compute_user_segments(target_user_id uuid) that:
4// - Counts events by type for the user in the last 30 days
5// - Assigns segments based on thresholds: 'power_user' (50+ events), 'active' (10-49 events), 'new_user' (<10 events)
6// - Checks event_data for purchase events to assign 'buyer' segment
7// - Updates user_profiles.segments array and updated_at timestamp
8// 2. A wrapper function recompute_all_segments() that loops through all users with recent events
9// 3. A pg_cron job that runs recompute_all_segments() every hour
10// 4. An RPC function increment_impressions(rule_id uuid) that atomically increments ab_tests.impressions
11// Also create an API route at app/api/segments/recompute/route.ts that manually triggers recomputation for testing.

Pro tip: Use Supavisor connection pooling (the pooled connection string from Supabase) in your Vars tab to prevent connection exhaustion when serverless functions compute segments concurrently.

Expected result: Segments are automatically recomputed every hour. Users are tagged with segments like power_user, active, new_user, and buyer based on their behavioral data.

6

Add the client-side event tracking hook

Create a React hook that your frontend components use to track events. This hook sends events to the tracking API and handles batching for performance. It uses useEffect to avoid SSR issues since it needs browser APIs.

hooks/use-event-tracker.ts
1'use client'
2
3import { useCallback, useRef, useEffect } from 'react'
4
5interface TrackEvent {
6 event_type: string
7 event_data?: Record<string, unknown>
8 page_url?: string
9}
10
11export function useEventTracker(userId: string | null) {
12 const queue = useRef<TrackEvent[]>([])
13
14 const flush = useCallback(async () => {
15 if (!userId || queue.current.length === 0) return
16 const events = [...queue.current]
17 queue.current = []
18
19 await Promise.allSettled(
20 events.map((event) =>
21 fetch('/api/events/track', {
22 method: 'POST',
23 headers: { 'Content-Type': 'application/json' },
24 body: JSON.stringify({ user_id: userId, ...event }),
25 })
26 )
27 )
28 }, [userId])
29
30 useEffect(() => {
31 const interval = setInterval(flush, 5000)
32 return () => {
33 clearInterval(interval)
34 flush()
35 }
36 }, [flush])
37
38 const track = useCallback(
39 (event_type: string, event_data?: Record<string, unknown>) => {
40 queue.current.push({
41 event_type,
42 event_data,
43 page_url: typeof window !== 'undefined' ? window.location.pathname : undefined,
44 })
45 },
46 []
47 )
48
49 return { track }
50}

Expected result: Components call track('button_click', { button: 'cta' }) and events are batched and sent to the API every 5 seconds for efficient network usage.

Complete code

app/api/personalization/evaluate/route.ts
1import { NextRequest, NextResponse } from 'next/server'
2import { createClient } from '@supabase/supabase-js'
3
4export const runtime = 'edge'
5
6export async function GET(req: NextRequest) {
7 const supabase = createClient(
8 process.env.SUPABASE_URL!,
9 process.env.SUPABASE_SERVICE_ROLE_KEY!
10 )
11
12 const userId = req.nextUrl.searchParams.get('user_id')
13 if (!userId) {
14 return NextResponse.json({ error: 'user_id required' }, { status: 400 })
15 }
16
17 const { data: profile } = await supabase
18 .from('user_profiles')
19 .select('segments, preferences')
20 .eq('user_id', userId)
21 .single()
22
23 if (!profile) {
24 return NextResponse.json({ variant: null, fallback: true })
25 }
26
27 const { data: rules } = await supabase
28 .from('personalization_rules')
29 .select('id, name, segment_criteria, content_variant, priority')
30 .eq('is_active', true)
31 .order('priority', { ascending: false })
32
33 const matchedRule = rules?.find((rule) => {
34 const criteria = rule.segment_criteria as { segments?: string[] }
35 return criteria.segments?.some((s: string) =>
36 profile.segments?.includes(s)
37 )
38 })
39
40 if (matchedRule) {
41 await supabase.rpc('increment_impressions', {
42 rule_id: matchedRule.id,
43 })
44 }
45
46 return NextResponse.json({
47 variant: matchedRule?.content_variant ?? null,
48 rule_name: matchedRule?.name ?? null,
49 fallback: !matchedRule,
50 })
51}

Customization ideas

Add real-time personalization

Use Supabase Realtime subscriptions to update content variants instantly when a user's segment changes, without waiting for a page refresh.

Integrate OpenAI for dynamic content generation

Instead of static content variants, call the OpenAI API to generate personalized copy based on the user's segment and preferences in real time.

Build a visual rule builder

Replace the JSON editor with a drag-and-drop condition builder UI where non-technical team members can create targeting rules visually.

Add multi-armed bandit optimization

Replace static A/B testing with a multi-armed bandit algorithm that automatically routes more traffic to winning variants over time.

Enable geographic targeting

Use the Vercel Edge middleware to detect user location from request headers and add geographic segments for region-specific personalization.

Common pitfalls

Pitfall: Exposing SUPABASE_SERVICE_ROLE_KEY with a NEXT_PUBLIC_ prefix

How to avoid: Store SUPABASE_SERVICE_ROLE_KEY in the Vars tab without any prefix. Only use it in API routes and Server Actions, never in client components.

Pitfall: Evaluating rules client-side instead of server-side

How to avoid: Always evaluate personalization rules in an API route or Server Action. The client only receives the final content variant, never the rules themselves.

Pitfall: Not using connection pooling for serverless segment computation

How to avoid: Use the Supavisor pooled connection string from your Supabase project settings. Set this as SUPABASE_URL in the Vars tab.

Pitfall: Accessing window or localStorage in Server Components for event tracking

How to avoid: Wrap event tracking in a 'use client' component and access window inside useEffect to ensure it only runs in the browser.

Best practices

  • Use Edge Runtime for the evaluation endpoint to minimize personalization latency globally
  • Batch event tracking calls on the client side to reduce network requests and improve page performance
  • Store personalization rules in the database rather than code so non-technical team members can update them
  • Use V0's prompt queuing to generate the event tracker, rule engine, and dashboard components in sequence without waiting
  • Always compute segments server-side using Supabase database functions to keep business logic secure and performant
  • Set up pg_cron for automatic segment recomputation rather than computing on every page load
  • Use V0's Design Mode (Option+D) to visually adjust the admin dashboard layout without spending credits
  • Test personalization rules with a small segment before rolling out to all users

AI prompts to try

Copy these prompts to build this project faster.

ChatGPT Prompt

I'm building a personalization engine with Next.js App Router and Supabase. I need help designing the segment computation logic. Given a user_events table with event_type and event_data columns, write a PostgreSQL function that assigns segments based on: purchase count (buyer if >0), event frequency (power_user if 50+/month, active if 10+), and recency (churning if no events in 14 days). Return the function and a pg_cron schedule.

Build Prompt

Create a real-time A/B testing dashboard. Use Supabase Realtime to subscribe to ab_tests table changes and update conversion rates live. Show a shadcn/ui Table with variant_key, impressions, conversions, rate, and a Badge that turns green when statistical significance exceeds 95% confidence. Include a Recharts BarChart comparing variants side by side.

Frequently asked questions

What is the minimum V0 plan needed for a personalization system?

V0 Free works for basic builds, but Premium ($20/month) is recommended because prompt queuing lets you generate multiple subsystems (event tracker, rule engine, dashboard) in sequence without waiting for each to finish.

Do I need pgvector for a basic personalization system?

No. For rule-based personalization with segment matching, standard Supabase tables are sufficient. pgvector is only needed if you want embedding-based user similarity for advanced content-based or hybrid personalization.

How do I handle personalization for anonymous users who haven't logged in?

Generate an anonymous ID using crypto.randomUUID() and store it in a cookie via Next.js middleware. Track events against this anonymous ID, then merge it with the authenticated user profile when they sign in.

Will the personalization engine slow down my page loads?

No, if you use Edge Runtime for the evaluation endpoint. Edge functions run globally on Vercel's network with sub-50ms latency. Pre-computed segments in user_profiles mean the evaluation is a simple database lookup, not a complex computation.

How do I deploy my personalization system to production?

Click Share then Publish to Production in V0 — it deploys to Vercel in 30-60 seconds. Alternatively, use the Git panel to connect to GitHub and create an auto-PR for team review before merging to production.

Can I run A/B tests without a third-party tool like Optimizely?

Yes. This build includes a built-in A/B testing framework with impression counting, conversion tracking, and statistical significance calculation — all stored in your own Supabase database with no external dependencies.

Can RapidDev help build a custom personalization system?

Yes. RapidDev has built 600+ apps including advanced personalization engines with real-time segment computation and ML-powered recommendation systems. Book a free consultation to discuss your specific requirements.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help building your app?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

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.