Build Guide
How to Build a Social Media App
You do not need to build the next Twitter — a niche social app for 10,000 users in one vertical is more valuable and 100× more achievable.
Social apps are the most architecturally complex consumer products: feeds, follows, notifications, media upload, and moderation all interact in non-obvious ways. Build a minimal version and you'll understand how every platform at scale actually works.
Data model
The core tables you'll need before writing any UI.
Build order
The sequence that minimises rewrites — build in this order.
User profiles and auth
Set up Supabase Auth. Create user profiles with username, bio, and avatar. Add a profile page that shows the user's posts. Build follow/unfollow — a simple join table between two user IDs.
Post creation
Build a post composer: text input, image upload (to Supabase Storage or Cloudinary), submit button. On submit: insert the post, update the user's post count, fan out to followers' feeds.
Feed — chronological first
Build the home feed as a simple query: SELECT posts WHERE user_id IN (following list) ORDER BY created_at DESC LIMIT 20. Get this working before any algorithm. Pagination via cursor (last post ID), not offset.
Likes and comments
Add like toggle — check if a like exists, insert or delete. Increment/decrement like_count on the post (denormalised for fast reads). Comments are just posts with a parent_id. Support one level of nesting first.
Notifications
Create a notifications table. On like: insert a notification for the post owner. On follow: insert a notification. On comment: insert a notification. Show an unread count badge in the nav. Mark all read on open.
Media upload
On image select, upload to Supabase Storage. Get back a public URL. Store URLs in the post's media_urls array. Show image previews in the composer before posting. Limit to 4 images per post.
Content moderation
Add a report button on posts and profiles. Store reports in a reports table. Build a simple /admin/reports queue that lets you review and delete reported content or ban users.
Done when
Observable behaviors that confirm V1 is complete — verify each one before you ship.
- ✓
User A posts; User B (who follows A) sees it in their feed without refreshing
- ✓
Like/unlike toggles update the count instantly via optimistic update — no loading spinner
- ✓
Following a user immediately makes their posts appear in the feed
- ✓
Notification badge updates within 5 seconds of receiving a like or comment
- ✓
App opens with 10 seeded posts from 3 demo users — feed is never empty on first login
First Run Requirement
Seed 3 demo user accounts with 10 posts across them. New user auto-follows all 3 demo users on signup so their feed is populated immediately. Demo content is clearly labelled.
Build it with AI — Architect Prompt
Paste this into Claude, Cursor, Windsurf, or any AI coding tool. It includes the full context of this guide — data model, build order, done conditions, and pitfalls — so the AI starts with everything it needs.
<context>
App: Social Media App
Difficulty: Advanced
Estimated build time: 2–4 weeks
Tech stack (intermediate): Next.js + Supabase (auth + DB + Realtime) + Cloudinary — niche community MVP in 2 weeks
Data model:
User: id, username, bio, avatar_url, follower_count, following_count, created_at
Post: id, user_id, content, media_urls[], like_count, comment_count, created_at, deleted_at
Follow: follower_id, following_id, created_at
Like: user_id, post_id, created_at
Comment: id, post_id, user_id, content, parent_id (for threads), created_at
Notification: id, user_id, type (like/comment/follow/mention), actor_id, post_id, read, created_at
FIRST RUN REQUIREMENT:
Seed 3 demo user accounts with 10 posts across them. New user auto-follows all 3 demo users on signup so their feed is populated immediately. Demo content is clearly labelled.
DONE WHEN — verify each before marking V1 complete:
□ User A posts; User B (who follows A) sees it in their feed without refreshing
□ Like/unlike toggles update the count instantly via optimistic update — no loading spinner
□ Following a user immediately makes their posts appear in the feed
□ Notification badge updates within 5 seconds of receiving a like or comment
□ App opens with 10 seeded posts from 3 demo users — feed is never empty on first login
Recommended build order:
1. Define API contract + schema + seed data (always first)
2. User profiles and auth — Set up Supabase Auth. Create user profiles with username, bio, and avatar. Add a profile page that shows the user's posts. Build follow/unfollow — a simple join table between two user IDs.
3. Post creation — Build a post composer: text input, image upload (to Supabase Storage or Cloudinary), submit button. On submit: insert the post, update the user's post count, fan out to followers' feeds.
4. Feed — chronological first — Build the home feed as a simple query: SELECT posts WHERE user_id IN (following list) ORDER BY created_at DESC LIMIT 20. Get this working before any algorithm. Pagination via cursor (last post ID), not offset.
5. Likes and comments — Add like toggle — check if a like exists, insert or delete. Increment/decrement like_count on the post (denormalised for fast reads). Comments are just posts with a parent_id. Support one level of nesting first.
6. Notifications — Create a notifications table. On like: insert a notification for the post owner. On follow: insert a notification. On comment: insert a notification. Show an unread count badge in the nav. Mark all read on open.
7. Media upload — On image select, upload to Supabase Storage. Get back a public URL. Store URLs in the post's media_urls array. Show image previews in the composer before posting. Limit to 4 images per post.
8. Content moderation — Add a report button on posts and profiles. Store reports in a reports table. Build a simple /admin/reports queue that lets you review and delete reported content or ban users.
9. End-to-end verification — walk every Done When condition above (always last)
Known pitfalls to avoid:
- Building a fan-out-on-write feed for large accounts — if a user has 1 million followers and posts, writing to 1M feed rows is slow. Use a hybrid: fan-out for small accounts, fan-in (query at read time) for accounts above a threshold.
- Using OFFSET for pagination — page 100 with OFFSET 2000 requires the DB to scan 2000 rows to skip them. Use cursor-based pagination: WHERE id < last_seen_id ORDER BY id DESC LIMIT 20.
- Storing like counts only in the likes table — counting likes with COUNT(*) on every feed load is slow. Keep a denormalised like_count on the post row and increment/decrement it atomically with each like/unlike.
</context>
<role>
You are a Senior Full-Stack Engineer and product architect who has shipped production Social Media Apps before. You know exactly where developers get stuck and how to structure the project to avoid rewrites.
</role>
<task id="step-1-clarify">
Before writing any code or spec, ask me 3–5 clarifying questions that will meaningfully change the architecture. Focus on: scale expectations, auth requirements, platform (web / mobile / both), must-have vs nice-to-have features for the MVP, and any hard constraints (budget, deadline, existing infrastructure).
<example>
BAD: "What tech stack do you want to use?" — too broad, doesn't change architecture decisions.
GOOD: "Do you need real-time sync across devices, or is single-device with periodic refresh acceptable? This decides whether we use WebSockets or simple REST polling and significantly affects infrastructure complexity."
</example>
⚠️ Do NOT start planning or writing code until I answer. Present the questions, then stop and wait.
</task>
<task id="step-2-architect">
After I answer your questions, produce the following in order:
1. TECHNICAL SPECIFICATION
- System architecture using → to show data and event flow
- Core data model (refer to data model in <context>)
- API contract — list ALL routes with request/response shapes BEFORE any implementation
WHY: agreeing on contracts first prevents rewrites when frontend and backend shapes diverge
- External APIs and integration points
2. MVP PLAN in three phases:
SETUP ✦ Step 1 (always): API contract + schema migration + seed data
Done when: schema is migrated, seed script runs, app starts with demo data, zero manual setup.
CORE FEATURES — build in this exact order:
2. User profiles and auth
3. Post creation
4. Feed — chronological first
5. Likes and comments
6. Notifications
7. Media upload
8. Content moderation
DONE WHEN — one observable condition per feature:
□ User A posts; User B (who follows A) sees it in their feed without refreshing
□ Like/unlike toggles update the count instantly via optimistic update — no loading spinner
□ Following a user immediately makes their posts appear in the feed
□ Notification badge updates within 5 seconds of receiving a like or comment
□ App opens with 10 seeded posts from 3 demo users — feed is never empty on first login
STRETCH GOALS — post-launch additions that are explicitly out of V1 scope.
3. BLOCKER ANALYSIS
Flag: API rate limits, auth complexity, scope risks, cold-start problems, top 2–3 failure modes.
FIRST RUN REQUIREMENT: Seed 3 demo user accounts with 10 posts across them. New user auto-follows all 3 demo users on signup so their feed is populated immediately. Demo content is clearly labelled.
✓ Done when: app launches with seed data and the full user journey works without any manual setup.
<self_check>
Before presenting your output, verify:
□ Every answer I gave to clarifying questions is reflected in the spec
□ Step 1 is ALWAYS: API contract + schema + seed — never UI first
□ Done When criteria are observable user behaviors, not internal states
□ The plan is realistic for one person to ship within 2–4 weeks
□ All known pitfalls from <context> are addressed in the spec
</self_check>
</task>
<task id="step-2.5-agents-md">
Generate an AGENTS.md file at the project root. This file is read automatically by Claude Code, Cursor, Windsurf, and all major AI coding tools at the start of every session.
Include:
- Project overview (2–3 sentences)
- Tech stack with exact version numbers
- Folder structure with one-line descriptions per directory
- Key architectural decisions and the WHY behind each
- Coding conventions: camelCase components, kebab-case files, max 200 lines per file, one concern per file
- Available commands: dev, build, test, lint, db:migrate, db:seed
- MVP scope boundaries — features explicitly out of V1
Note in the file: "Cursor users: symlink .cursorrules → AGENTS.md. Claude Code users: symlink CLAUDE.md → AGENTS.md."
</task>
<task id="step-3-implement">
Implement the full project in the exact order from step 2. For each step:
- Write complete code (no placeholders or TODOs)
- Confirm the step works before moving to the next
<constraints>
- Step 1 is ALWAYS: define all API routes + TypeScript request/response interfaces + run schema migration
WHY: agreeing on contracts before writing a single component prevents shape mismatches that require rewrites
- Final step is ALWAYS: start the app and walk every Done When condition from <context> end-to-end
WHY: a spec that passes unit tests but breaks the core user journey is not done
- Max 200 lines per file. WHY: every file must fit in one AI context window for complete reasoning
- One concern per file. WHY: mixing auth logic into API routes makes it impossible to reuse — extract to lib/auth.ts
- TypeScript strict mode, no `any` types. WHY: catches data shape mismatches at compile time, not in production
- All database queries in server components or API routes only. WHY: keeps credentials server-side
- All environment variables documented in .env.example. WHY: next developer sets up in under 5 minutes
</constraints>
</task>How to use this prompt
- 1.Copy the prompt above
- 2.Open Claude, Cursor (Cmd+L), or Windsurf
- 3.Paste the prompt and send — the AI will ask 3–5 clarifying questions
- 4.Answer the questions, then it generates your full project spec
- 5.Continue in the same session to implement step by step
Common mistakes
What trips up most developers building this for the first time.
Building a fan-out-on-write feed for large accounts — if a user has 1 million followers and posts, writing to 1M feed rows is slow. Use a hybrid: fan-out for small accounts, fan-in (query at read time) for accounts above a threshold.
Using OFFSET for pagination — page 100 with OFFSET 2000 requires the DB to scan 2000 rows to skip them. Use cursor-based pagination: WHERE id < last_seen_id ORDER BY id DESC LIMIT 20.
Storing like counts only in the likes table — counting likes with COUNT(*) on every feed load is slow. Keep a denormalised like_count on the post row and increment/decrement it atomically with each like/unlike.
Recommended tech stack
Pick the level that matches your experience.
Next.js + Supabase (auth + DB + Realtime) + Cloudinary — niche community MVP in 2 weeks
Next.js + Supabase + Redis (feed caching) + BullMQ (notification queue) + Cloudflare R2 (media)
Take it further — related ideas
Each comes with revenue math, a full build guide, and a prompt to paste into Claude or Cursor.