How to Build a Social Media App with Next.js and Notify

In this guide, we'll walk through building a modern social media application using Next.js 14's App Router, Tailwind CSS for styling, Shadcn components for the UI, and Notify for handling transactional emails.

Prerequisites

  • Basic knowledge of Next.js, Tailwind CSS, and Shadcn components
  • Node.js and npm installed

Project Setup

First, create a new Next.js project with the App Router:

npx create-next-app@latest social-media-app --typescript --tailwind --app

Install the required dependencies:

npm install @radix-ui/react-dialog @radix-ui/react-dropdown-menu @radix-ui/react-avatar notifycx

Project Structure

app/
  (auth)/
    sign-in/page.tsx
    sign-up/page.tsx
  (main)/
    feed/page.tsx
    profile/[username]/page.tsx
  api/
    posts/route.ts
    follow/route.ts
components/
  ui/
    post-card.tsx
    user-card.tsx
lib/
  email-templates.ts

Authentication Pages

Let's create a sign-up page with email verification using Notify for sending verification emails:

// app/(auth)/sign-up/page.tsx
'use client';

import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Card, CardHeader, CardContent } from '@/components/ui/card';

export default function SignUpPage() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [username, setUsername] = useState('');

  const handleSignUp = async (e: React.FormEvent) => {
    e.preventDefault();

    try {
      // Create user in your database
      const user = await createUser({ email, password, username });

      // Send verification email using Notify
      await fetch('/api/send-verification', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          email,
          username,
          verificationToken: user.verificationToken
        })
      });
    } catch (error) {
      console.error('Sign up failed:', error);
    }
  };

  return (
    <div className="min-h-screen flex items-center justify-center">
      <Card className="w-[400px]">
        <CardHeader>
          <h1 className="text-2xl font-bold">Create an Account</h1>
        </CardHeader>
        <CardContent>
          <form onSubmit={handleSignUp} className="space-y-4">
            <Input
              placeholder="Username"
              value={username}
              onChange={(e) => setUsername(e.target.value)}
            />
            <Input
              type="email"
              placeholder="Email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
            />
            <Input
              type="password"
              placeholder="Password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
            />
            <Button type="submit" className="w-full">
              Sign Up
            </Button>
          </form>
        </CardContent>
      </Card>
    </div>
  );
}

Email Verification API

Create an API route to handle sending verification emails using Notify:

// app/api/send-verification/route.ts
import Notify from 'notifycx';

const notify = new Notify(process.env.NOTIFY_API_KEY);

export async function POST(req: Request) {
  const { email, username, verificationToken } = await req.json();

  try {
    await notify.sendEmailFromTemplate({
      to: email,
      templateId: 'verification-email-template',
      variables: {
        username,
        verificationLink: `${process.env.NEXT_PUBLIC_APP_URL}/verify?token=${verificationToken}`
      }
    });

    return Response.json({ success: true });
  } catch (error) {
    return Response.json(
      { error: 'Failed to send verification email' },
      { status: 500 }
    );
  }
}

Email Verification API

Create an API route to handle sending verification emails using Notify:

// app/api/send-verification/route.ts
import Notify from 'notifycx';

const notify = new Notify(process.env.NOTIFY_API_KEY);

export async function POST(req: Request) {
  const { email, username, verificationToken } = await req.json();

  try {
    await notify.sendEmailFromTemplate({
      to: email,
      templateId: 'verification-email-template',
      variables: {
        username,
        verificationLink: `${process.env.NEXT_PUBLIC_APP_URL}/verify?token=${verificationToken}`
      }
    });

    return Response.json({ success: true });
  } catch (error) {
    return Response.json(
      { error: 'Failed to send verification email' },
      { status: 500 }
    );
  }
}

Post Component

Create a reusable post component:

// components/ui/post-card.tsx
import { Card, CardContent, CardFooter } from '@/components/ui/card';
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
import { Button } from '@/components/ui/button';
import { Heart, MessageCircle, Share } from 'lucide-react';

interface PostCardProps {
  username: string;
  avatar: string;
  content: string;
  likes: number;
  comments: number;
}

export function PostCard({
  username,
  avatar,
  content,
  likes,
  comments
}: PostCardProps) {
  return (
    <Card className="mb-4">
      <CardContent className="pt-4">
        <div className="flex items-center gap-3 mb-4">
          <Avatar>
            <AvatarImage src={avatar} />
            <AvatarFallback>{username[0]}</AvatarFallback>
          </Avatar>
          <span className="font-semibold">{username}</span>
        </div>
        <p className="text-gray-600 dark:text-gray-300">{content}</p>
      </CardContent>
      <CardFooter className="flex gap-4">
        <Button variant="ghost" size="sm">
          <Heart className="w-4 h-4 mr-2" />
          {likes}
        </Button>
        <Button variant="ghost" size="sm">
          <MessageCircle className="w-4 h-4 mr-2" />
          {comments}
        </Button>
        <Button variant="ghost" size="sm">
          <Share className="w-4 h-4" />
        </Button>
      </CardFooter>
    </Card>
  );
}

Feed Page

Create the main feed page:

// app/(main)/feed/page.tsx
import { PostCard } from '@/components/ui/post-card';

async function getPosts() {
  // Fetch posts from your database
  return [
    {
      id: 1,
      username: 'johndoe',
      avatar: '/avatars/john.jpg',
      content: 'Just launched my new project!',
      likes: 42,
      comments: 7
    }
    // ... more posts
  ];
}

export default async function FeedPage() {
  const posts = await getPosts();

  return (
    <div className="max-w-2xl mx-auto py-8 px-4">
      <div className="space-y-6">
        {posts.map((post) => (
          <PostCard key={post.id} {...post} />
        ))}
      </div>
    </div>
  );
}

Notification Emails

When users receive interactions (likes, comments, follows), we'll send notification emails using Notify. Here's how to set up the notification system:

// lib/email-templates.ts
import Notify from 'notifycx';

const notify = new Notify(process.env.NOTIFY_API_KEY);

export async function sendInteractionNotification(
  to: string,
  type: 'like' | 'comment' | 'follow',
  actorUsername: string
) {
  const templates = {
    like: 'like-notification-template',
    comment: 'comment-notification-template',
    follow: 'follow-notification-template'
  };

  await notify.sendEmailFromTemplate({
    to,
    templateId: templates[type],
    variables: {
      actorUsername,
      action: type === 'follow' ? 'followed you' : `${type}d your post`
    }
  });
}

Conclusion

This is a basic setup for a social media app using Next.js, Tailwind, and Notify for transactional emails. You can expand upon this by adding:

  • Real-time updates using WebSockets
  • Image upload functionality
  • Direct messaging
  • Advanced feed algorithms
  • More email notifications for different events

Remember to create your email templates in the Notify dashboard for:

  • User verification
  • Welcome emails
  • Interaction notifications
  • Password reset emails

The complete code for this project would be more extensive, but this gives you a solid foundation to build upon. For more information about sending emails with Notify, refer to the Notify Documentation.