Stop 19 · Build Full-Stack Portfolio ProjectsProjects · 3/12

Social Media App (Full Stack)

Part of the Full Stack Developer Roadmap

In short

Likes, follows, comments and a feed: everyone knows how a social app should feel, so every lag shows. It's great practice for connected data and instant-feeling updates.

Posts, a feed, comments, likes and profiles — practice for connected data, real-time-ish updates, and building an app people intuitively understand how to use.

The purple blob pins a photo to a cork board covered with friends' photos and little hearts.

Think of it like this

The hostel corridor pinboard

Someone pins a trip photo, friends stick little hearts on it and write notes below. You walk past and see the newest photos from people you know. That board is your feed, and the hearts are likes.

Watch it happen

Tapping like, the optimistic way

Browser
API
Database
heart fills, 41 → 42

1/5The UI updates at once, before the server replies. That's an optimistic update.

The big ideas

What you need to know

Follows are a join table

A follows table with follower_id and following_id. The feed is posts by people in that list, newest first.

Likes need a unique pair

One row per (user, post) with a unique constraint, plus a stored count so the feed doesn't count every time.

Feel instant

Optimistic likes and comments make the app feel fast. Roll back cleanly if the server says no.

Uploads go direct

Upload photos straight to storage with a signed URL, then save only the URL in your database.

See it in code

Walk through the code

tsx
1"use client";2import { useOptimistic, startTransition } from "react";3import { toggleLike } from "./actions";4 5export function LikeButton({ postId, liked, count }: { postId: string; liked: boolean; count: number }) {6  const [state, flip] = useOptimistic({ liked, count }, (s) => ({7    liked: !s.liked,8    count: s.count + (s.liked ? -1 : 1),9  }));10  const onClick = () => startTransition(async () => {11    flip(null);12    await toggleLike(postId); // server action: writes to the DB, then revalidates13  });14  return <button onClick={onClick}>{state.liked ? "Liked" : "Like"} · {state.count}</button>;15}

1/3The optimistic version of the state: flip liked and move the count by one.

React's useOptimistic shows the new like straight away. If the server action fails, React drops the optimistic state and the real value shows again.

Save yourself the pain

Common mistakes

Oops

Running COUNT(*) on the likes table for every post in the feed.

Do this instead

Keep a like_count column on posts, updated in the same transaction as the like row.

Oops

Loading every post from everyone and filtering the feed in React.

Do this instead

Build the feed query on the server with a JOIN on follows and cursor pagination.

Your mission

First milestone: post, follow, like

~45 min

Quick check

A user double-taps like very fast and two requests reach the server. How do you stop the like counting twice?

Go deeper

  1. 1Implement an infinite-scrolling feed with proper pagination.
  2. 2Add optimistic UI for likes and comments so interactions feel instant.
  3. 3Build a profile page showing a user's own posts and activity.

More projects at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge