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.