A full-stack, production-ready blog application built with Next.js 16, featuring real-time interactions, rich text editing, and a comprehensive content management system.
- 📰 Article Management - Create, edit, delete, and publish articles with rich text editor
- 🔍 Advanced Search - Real-time search across articles with filtering capabilities
- 💬 Comments System - Engage with readers through nested comments
- ❤️ Like & Save - Interactive engagement with like/dislike and bookmark features
- 📊 Analytics Dashboard - Track article performance and user engagement
- 🎨 Rich Text Editor - Powered by React Quill for professional content creation
- 🖼️ Image Management - Cloudinary integration for optimized image uploads
- 🔐 Authentication - Secure user authentication via Clerk
- 🌓 Dark Mode - Seamless theme switching with next-themes
- 📱 Responsive Design - Mobile-first approach with Tailwind CSS
- ⚡ Performance - Optimized with Next.js 16 App Router and Server Components
- ♿ Accessibility - Built with Radix UI primitives for WCAG compliance
- Framework: Next.js 16 (App Router)
- Language: TypeScript 5
- Styling: Tailwind CSS 4
- UI Components: Radix UI, shadcn/ui
- Rich Text: React Quill
- Icons: Lucide React
- Database: PostgreSQL
- ORM: Prisma 5.22
- Authentication: Clerk
- File Storage: Cloudinary
- Validation: Zod
- Package Manager: npm
- Linting: ESLint 9
- Type Checking: TypeScript
- Database Seeding: tsx
Before you begin, ensure you have the following installed:
- Node.js 18.x or higher
- npm or yarn or pnpm
- PostgreSQL database
- Git
git clone <repository-url>
cd blognpm installCreate a .env file in the root directory:
# Database
DATABASE_URL="postgresql://user:password@localhost:5432/blog_db"
# Clerk Authentication
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_clerk_publishable_key
CLERK_SECRET_KEY=your_clerk_secret_key
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
# Cloudinary
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret# Generate Prisma Client
npx prisma generate
# Run migrations
npx prisma migrate dev
# Seed the database (optional)
npm run seednpm run devVisit http://localhost:3000 to see your application.
blog/
├── actions/ # Server actions for data mutations
│ ├── create-article.ts
│ ├── edit-article.ts
│ ├── delete-article.ts
│ ├── like-dislike.ts
│ ├── save-article.ts
│ ├── create-comment.ts
│ └── search.ts
├── app/ # Next.js App Router
│ ├── (home)/ # Public home pages
│ ├── about/ # About page
│ ├── api/ # API routes
│ ├── article/ # Article detail pages
│ ├── dashboard/ # User dashboard
│ └── layout.tsx # Root layout
├── components/ # React components
│ ├── articles/ # Article-related components
│ ├── comments/ # Comment components
│ ├── dashboard/ # Dashboard components
│ ├── home/ # Home page components
│ └── ui/ # Reusable UI components
├── hooks/ # Custom React hooks
├── lib/ # Utility functions
│ ├── prisma.ts # Prisma client
│ ├── query/ # Database queries
│ └── utils.ts # Helper functions
├── prisma/ # Database schema and migrations
│ ├── schema.prisma
│ └── seed.ts
└── public/ # Static assets
# Development
npm run dev # Start development server
npm run dev:turbo # Start with Turbo mode
# Production
npm run build # Build for production
npm run start # Start production server
# Database
npm run seed # Seed database with sample data
npx prisma studio # Open Prisma Studio
npx prisma migrate dev # Run migrations
# Code Quality
npm run lint # Run ESLintmodel User {
id String @id @default(uuid())
clearkUserId String @unique
email String @unique
name String?
imageURL String?
articles Article[]
Comment Comment[]
likes Like[]
savedArticles SavedArticle[]
}- Stores user profile information
- Linked to Clerk authentication
- Manages user-generated content
model Article {
id String @id @default(uuid())
title String
content String
category String
featuredImage String
authorId String
author User @relation(fields: [authorId], references: [id])
comments Comment[]
likes Like[]
savedBy SavedArticle[]
createAt DateTime @default(now())
updatedAt DateTime @updatedAt
}- Main content entity
- Rich text content support
- Automatic timestamps
model Comment {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id])
articleId String
article Article @relation(fields: [articleId], references: [id])
body String
createdAt DateTime @default(now())
}- User comments on articles
- Linked to both user and article
model Like {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id])
articleId String
article Article @relation(fields: [articleId], references: [id])
createdAt DateTime @default(now())
@@unique([userId, articleId])
}- One like per user per article
- Unique constraint prevents duplicates
model SavedArticle {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id])
articleId String
article Article @relation(fields: [articleId], references: [id])
createdAt DateTime @default(now())
@@unique([userId, articleId])
}- Bookmark functionality
- Unique constraint per user-article pair
- One-to-Many: User → Articles, User → Comments
- Many-to-Many: Users ↔ Articles (via Likes and SavedArticles)
- Cascade Deletes: Configured for data integrity
Create Article
// actions/create-article.ts
createArticle(formData: FormData)
- Input: title, content, category, featuredImage
- Returns: Created article or error
- Auth: RequiredEdit Article
// actions/edit-article.ts
editArticle(articleId: string, formData: FormData)
- Input: articleId, updated fields
- Returns: Updated article or error
- Auth: Required (must be author)Delete Article
// actions/delete-article.ts
deleteArticle(articleId: string)
- Input: articleId
- Returns: Success or error
- Auth: Required (must be author)Like/Dislike Article
// actions/like-dislike.ts
toggleLike(articleId: string)
- Input: articleId
- Returns: Updated like status
- Auth: RequiredSave Article
// actions/save-article.ts
toggleSave(articleId: string)
- Input: articleId
- Returns: Updated save status
- Auth: RequiredSearch Articles
// actions/search.ts
searchArticles(query: string)
- Input: search query
- Returns: Matching articles
- Auth: OptionalCreate Comment
// actions/create-comment.ts
createComment(articleId: string, body: string)
- Input: articleId, comment body
- Returns: Created comment
- Auth: RequiredFetch Articles
// lib/query/fetch-articles.ts
- fetchAllArticles(): Get all published articles
- fetchArticleById(id): Get single article with relations
- fetchUserArticles(userId): Get articles by author
- fetchSavedArticles(userId): Get user's saved articles- ArticleCard - Display article preview
- ArticleDetailPage - Full article view
- CreateArticlePage - Article creation form
- EditArticlePage - Article editing form
- AllArticlePage - Article listing with filters
- LikeButton - Interactive like/dislike button
- ArticleSearchInput - Search functionality
- CommentsList - Display all comments
- CommentInput - Add new comment
- CommentItem - Single comment display
- BlogDashboard - Main
- Users sign up/sign in via Clerk
- Clerk webhook creates user record in database
- User sessions managed by Clerk middleware
- Protected routes secured via middleware.ts
Built with shadcn/ui and Radix UI for:
- Accessible, keyboard-navigable interfaces
- Consistent design system
- Dark mode support
- Responsive layouts
# Install Vercel CLI
npm i -g vercel
# Deploy
vercelEnsure all environment variables are configured in your deployment platform:
- Database connection string
- Clerk API keys
- Cloudinary credentials
npx prisma migrate deploy┌─────────────────────────────────────────────────────────────┐
│ Development Lifecycle │
└─────────────────────────────────────────────────────────────┘
1. Setup Environment
├── Clone repository
├── Install dependencies (npm install)
├── Configure .env file
├── Setup PostgreSQL database
└── Run migrations (npx prisma migrate dev)
2. Local Development
├── Start dev server (npm run dev)
├── Access at http://localhost:3000
├── Hot reload enabled
└── Prisma Studio for DB inspection (npx prisma studio)
3. Feature Development
├── Create feature branch (git checkout -b feature/name)
├── Implement changes
├── Test locally with seeded data
├── Check for type errors (TypeScript)
└── Run linter (npm run lint)
4. Code Review & Merge
├── Commit changes (git commit -m "message")
├── Push to remote (git push origin feature/name)
├── Create Pull Request
├── Code review by team
└── Merge to main branch
5. Deployment
├── Auto-deploy on merge (Vercel)
├── Run production migrations
├── Verify environment variables
└── Monitor deployment logs
┌─────────────────────────────────────────────────────────────┐
│ Author Journey │
└─────────────────────────────────────────────────────────────┘
Step 1: Authentication
└── Sign in/Sign up via Clerk
├── Email/Password
├── Google OAuth
└── GitHub OAuth
Step 2: Dashboard Access
└── Navigate to /dashboard
├── View recent articles
├── Check analytics
└── Access saved articles
Step 3: Create Article
└── Click "Create Article"
├── Enter title
├── Select category
├── Upload featured image (Cloudinary)
└── Write content (Rich Text Editor)
├── Format text (bold, italic, headings)
├── Add links
├── Insert images
└── Create lists
Step 4: Publish
└── Submit article
├── Validation (Zod schema)
├── Save to database (Prisma)
└── Redirect to article page
Step 5: Manage Content
└── Article Management
├── Edit existing articles
├── Delete articles
├── View engagement metrics
└── Respond to comments
┌─────────────────────────────────────────────────────────────┐
│ Reader Journey │
└─────────────────────────────────────────────────────────────┘
Step 1: Discovery
└── Landing Page (/)
├── Browse featured articles
├── View top articles
├── Search by keyword
└── Filter by category
Step 2: Read Article
└── Article Detail Page (/article/[id])
├── Read full content
├── View author info
├── Check publish date
└── See engagement stats
Step 3: Interact
└── Engagement Options
├── Like/Dislike article
├── Save for later
├── Share article
└── Leave comment
Step 4: Community
└── Comments Section
├── Read other comments
├── Reply to comments
├── Engage in discussions
└── Follow conversations
┌─────────────────────────────────────────────────────────────┐
│ Data Flow │
└─────────────────────────────────────────────────────────────┘
User Action → Server Action → Prisma ORM → PostgreSQL
Example: Creating an Article
1. User submits form
└── /dashboard/article/create
2. Server Action (create-article.ts)
├── Validate input (Zod)
├── Check authentication (Clerk)
└── Process data
3. Prisma Query
├── prisma.article.create()
├── Include relations (author)
└── Return created article
4. Database Update
├── Insert into Article table
├── Update foreign keys
└── Commit transaction
5. Response
├── Revalidate cache
├── Redirect to article
└── Show success toast
┌─────────────────────────────────────────────────────────────┐
│ Auth Flow (Clerk) │
└─────────────────────────────────────────────────────────────┘
1. User Access
└── Visit protected route
└── middleware.ts checks auth
2. Not Authenticated
└── Redirect to /sign-in
├── Show Clerk sign-in UI
└── Multiple auth methods
3. Sign In/Sign Up
└── Clerk handles authentication
├── Verify credentials
├── Create session
└── Generate JWT token
4. Webhook (Optional)
└── Clerk sends user.created event
└── Create User in database
├── Store Clerk user ID
├── Save email & profile
└── Initialize user data
5. Authenticated
└── Access granted
├── Session stored in cookies
├── User data available
└── Protected routes accessible
┌─────────────────────────────────────────────────────────────┐
│ Cloudinary Integration │
└─────────────────────────────────────────────────────────────┘
1. User Selects Image
└── File input in article form
2. Client-Side Upload
└── next-cloudinary component
├── Validate file type/size
├── Show upload progress
└── Generate preview
3. Cloudinary Processing
└── Upload to cloud
├── Optimize image
├── Generate transformations
├── Create responsive URLs
└── Return secure URL
4. Store URL
└── Save to database
├── featuredImage field
└── Associated with article
5. Display Image
└── Render on frontend
├── Responsive images
├── Lazy loading
└── Optimized delivery
┌─────────────────────────────────────────────────────────────┐
│ Search Implementation │
└─────────────────────────────────────────────────────────────┘
1. User Input
└── Search bar (header or article page)
└── Type search query
2. Real-time Search
└── Debounced input
├── Wait for user to stop typing
└── Trigger search action
3. Server Action (search.ts)
└── Query database
├── Search in title
├── Search in content
├── Search in category
└── Filter by relevance
4. Prisma Query
└── prisma.article.findMany()
├── WHERE clause with contains
├── Include author data
└── Order by relevance
5. Display Results
└── Update UI
├── Show matching articles
├── Highlight search terms
├── Show result count
└── Handle no results
┌─────────────────────────────────────────────────────────────┐
│ Deployment Pipeline │
└─────────────────────────────────────────────────────────────┘
1. Code Push
└── git push origin main
2. Vercel Detection
└── Webhook triggered
└── Start build process
3. Build Phase
├── Install dependencies
├── Run TypeScript compilation
├── Run ESLint
├── Generate Prisma Client
└── Build Next.js app (npm run build)
4. Database Migration
└── npx prisma migrate deploy
├── Apply pending migrations
└── Update production schema
5. Deployment
├── Deploy to Vercel edge network
├── Set environment variables
├── Configure domains
└── Enable CDN
6. Post-Deployment
├── Run health checks
├── Monitor error logs
├── Verify functionality
└── Notify team (optional)
┌─────────────────────────────────────────────────────────────┐
│ Client (Browser) │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Pages │ │ Components │ │ Hooks │ │
│ └────────────┘ └────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────────┐
│ Next.js App Router │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Routes │ │ Server │ │ API │ │
│ │ │ │ Actions │ │ Routes │ │
│ └────────────┘ └────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────────┐
│ Business Logic │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Prisma │ │ Zod │ │ Utils │ │
│ │ ORM │ │ Validation │ │ │ │
│ └────────────┘ └────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────────┐
│ External Services │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ PostgreSQL │ │ Clerk │ │ Cloudinary │ │
│ │ Database │ │ Auth │ │ Images │ │
│ └────────────┘ └────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────┘
- Server Components - Default rendering strategy for performance
- Server Actions - Type-safe mutations without API routes
- Optimistic Updates - Instant UI feedback before server confirmation
- Middleware - Route protection and authentication checks
- Component Composition - Reusable UI components with Radix UI
- Type Safety - End-to-end TypeScript with Prisma and Zod
-
Authentication & Authorization
- Clerk handles secure authentication
- JWT tokens for session management
- Protected routes via middleware
- User-specific data access controls
-
Data Validation
- Zod schemas for input validation
- Server-side validation on all mutations
- SQL injection prevention via Prisma
- XSS protection with React
-
Environment Variables
- Sensitive data in .env (not committed)
- Different configs for dev/prod
- Secure API key management
-
Database Security
- Parameterized queries via Prisma
- Unique constraints on critical fields
- Foreign key relationships enforced
- Connection pooling for performance
# Install testing dependencies
npm install -D vitest @testing-library/react @testing-library/jest-dom
# Run tests
npm run test- Test server actions with database
- Mock Clerk authentication
- Test Prisma queries
# Install Playwright
npm install -D @playwright/test
# Run E2E tests
npx playwright test-
Next.js Features
- App Router for automatic code splitting
- Server Components reduce client bundle
- Image optimization with next/image
- Font optimization with next/font
-
Database
- Prisma connection pooling
- Indexed fields for fast queries
- Efficient relations with include/select
-
Caching
- Next.js automatic caching
- Revalidation strategies
- Static generation where possible
-
Assets
- Cloudinary CDN for images
- Responsive image delivery
- Lazy loading components
Database Connection Error
# Check DATABASE_URL in .env
# Ensure PostgreSQL is running
# Verify database exists
npx prisma db pushClerk Authentication Issues
# Verify Clerk keys in .env
# Check middleware.ts configuration
# Ensure public routes are configuredBuild Errors
# Clear Next.js cache
rm -rf .next
# Regenerate Prisma Client
npx prisma generate
# Rebuild
npm run buildModule Not Found
# Clear node_modules and reinstall
rm -rf node_modules package-lock.json
npm install- Vercel Analytics - Built-in performance monitoring
- Sentry - Error tracking and monitoring
- Prisma Pulse - Real-time database events
- LogRocket - Session replay and debugging
# Feature development
git checkout -b feature/new-feature
git add .
git commit -m "feat: add new feature"
git push origin feature/new-feature
# Bug fixes
git checkout -b fix/bug-description
git commit -m "fix: resolve bug description"
# Hotfix
git checkout -b hotfix/critical-issue
git commit -m "hotfix: critical issue fix"Follow Conventional Commits:
feat:- New featurefix:- Bug fixdocs:- Documentation changesstyle:- Code style changesrefactor:- Code refactoringtest:- Test additions/changeschore:- Build process or auxiliary tool changes
Contributions are welcome! Please follow these steps:
-
Fork the repository
# Click "Fork" on GitHub git clone https://github.com/your-username/blog.git -
Create a feature branch
git checkout -b feature/amazing-feature
-
Make your changes
- Follow code style guidelines
- Add tests if applicable
- Update documentation
-
Commit your changes
git commit -m 'feat: add amazing feature' -
Push to the branch
git push origin feature/amazing-feature
-
Open a Pull Request
- Describe your changes
- Link related issues
- Wait for review
- Use TypeScript for type safety
- Follow ESLint rules
- Use meaningful variable names
- Add comments for complex logic
- Keep components small and focused
- Write reusable utility functions
This project is licensed under the MIT License.
- Next.js - React framework
- Prisma - Database ORM
- Clerk - Authentication
- shadcn/ui - UI components
- Cloudinary - Media management
For questions or support, please open an issue on GitHub.
Built with ❤️ using Next.js and modern web technologies