Blog Template
A production-ready, full-stack blog platform built with Next.js, React 19, MongoDB, and Cloudinary. Ships with a complete admin dashboard, rich text editor, newsletter subscriptions, contact forms, and a fully customizable public blog interface.
Overview
Blog Template is a scalable, full-stack blog platform with a dual-interface architecture — a public-facing blog site and a private admin dashboard. It is built on the Next.js App Router with server components, server actions, and API routes. All data is stored in MongoDB, and media files are managed via Cloudinary CDN.
The template is designed to be purchased, configured, and deployed with minimal setup. The admin dashboard provides a complete content management system so non-technical users can manage all blog content without touching the codebase.
Features
Public Interface
Home Page
Hero banners, featured posts, editor picks, trending articles, category navigation
Blog Posts
Full post pages with view tracking, estimated read time, related articles
Search
Full-text search across all published blog posts
Newsletter
Email subscription modal with backend subscriber management
Contact Form
Contact page with message submission and admin inbox
Dark / Light Theme
System-aware theme toggle with persistence
Admin Dashboard
Dashboard Stats
Overview of blogs, categories, authors, subscribers, and messages
Blog Management
Create, edit, publish, draft, and delete posts with rich text editor
Category Management
Drag-and-drop reordering, active/inactive toggle
Author Management
Create and manage author profiles with avatar images
Home Page Editor
Edit banners, taglines, images, and editor picks directly
Global Settings
Company info, Cloudinary CDN, SEO metadata, terms & policy
Tech Stack
| Category | Technology | Version |
|---|---|---|
| Framework | Next.js (App Router) | 16.1.6 |
| Runtime | React | 19.1.0 |
| Language | TypeScript | 5.x |
| Database | MongoDB (Mongoose) | 8.19.3 |
| Authentication | NextAuth.js | 5.0.0-beta.30 |
| Image CDN | Cloudinary | 2.8.0 |
| Styling | Tailwind CSS | 4.x |
| UI Primitives | Shadcn/ui + Radix UI | Latest |
| State Management | Zustand | 5.0.8 |
| Forms | React Hook Form | 7.62.0 |
| Validation | Zod | 4.1.5 |
| Rich Text Editor | SunEditor | 2.47.8 |
| Drag and Drop | DnD Kit | 6.x / 10.x |
| Table | TanStack Table | 8.21.3 |
| Icons | Lucide React + React Icons | Latest |
| Notifications | React Hot Toast | 2.6.0 |
| Date Utilities | date-fns, moment-timezone | Latest |
| Security | bcrypt, jsonwebtoken | Latest |
Requirements
- Node.js >= 18.x
- MongoDB — Atlas cluster or local MongoDB instance
- Cloudinary — free or paid account (configured via admin dashboard)
- pnpm (recommended) or npm / yarn
Installation
-
Extract or clone the project
cd blog-template -
Install dependencies
pnpm install # or npm install -
Create environment variables
Create a.env.localfile in the project root — see Environment Variables. -
Start development server
Application runs atpnpm devhttp://localhost:3000 -
Login to admin dashboard
Visithttp://localhost:3000/admin/loginwith default credentials:
Email:admin@example.com| Password:ChangeMe123! -
Configure Cloudinary
Go to Settings → Cloudinary in the admin dashboard and enter your Cloudinary credentials.
Environment Variables
Create a .env.local file in the project root:
MONGODB_URI=mongodb+srv://<username>:<password>@<cluster>.mongodb.net/<dbname>?retryWrites=true&w=majority
NEXTAUTH_SECRET=your_nextauth_secret_here
NEXTAUTH_URL=http://localhost:3000
JWT_SECRET=your_jwt_secret_here
openssl rand -base64 32. Requiredhttp://localhost:3000 for development. Requiredopenssl rand -base64 32. RequiredCloudinary credentials are stored in MongoDB via the admin settings panel. They do not need to be added to .env.local.
Default Admin Credentials
A default admin user is automatically seeded into the database on first connection if no admin exists.
admin@example.comChangeMe123!Change the default admin password immediately after your first login. Navigate to Settings → Security in the admin dashboard.
Project Structure
blog-template/
├── app/ # Next.js App Router root
│ ├── layout.tsx # Root layout
│ ├── globals.css # Global styles
│ ├── icon.tsx # App icon
│ ├── api/ # API route handlers
│ │ ├── auth/[...nextauth]/ # NextAuth.js handler
│ │ ├── admin/ # Protected admin APIs
│ │ │ ├── auth/ # Admin authentication
│ │ │ ├── blog/ # Blog CRUD
│ │ │ ├── category/ # Category CRUD
│ │ │ ├── author/ # Author CRUD
│ │ │ ├── contact-us/ # Contact messages
│ │ │ ├── feedback/ # Feedback management
│ │ │ ├── home-section/ # Home section editor
│ │ │ ├── settings/ # App settings
│ │ │ ├── subscribe/ # Subscribers list
│ │ │ └── dashboard/ # Dashboard stats
│ │ ├── blog/ # Public blog APIs
│ │ ├── category/ # Public category API
│ │ ├── author/ # Public author APIs
│ │ ├── banner/ # Banner API
│ │ ├── contact-us/ # Contact form API
│ │ ├── feedback/ # Feedback API
│ │ ├── subscribe/ # Subscription API
│ │ ├── search/ # Search API
│ │ ├── menu/ # Navigation menu API
│ │ ├── home-section/ # Home section API
│ │ ├── settings/ # Public settings API
│ │ └── cache/ # Cache invalidation
│ ├── (auth)/ # Authentication pages (no layout)
│ │ └── admin/login/ # Admin login page
│ ├── (private)/ # Protected admin interface
│ │ ├── layout.tsx # Dashboard layout (auth guard)
│ │ └── admin/dashboard/ # Dashboard pages
│ │ ├── page.tsx # Dashboard overview
│ │ ├── blogs/ # Blog management
│ │ ├── categories/ # Category management
│ │ ├── author/ # Author management
│ │ ├── settings/ # Global settings
│ │ ├── contact-us/ # Contact inbox
│ │ ├── feedback/ # Feedback list
│ │ ├── subscribe/ # Subscriber list
│ │ └── (showCase)/ # Homepage showcase editor
│ │ ├── banner-one/ # Banner 1 editor
│ │ ├── banner-two/ # Banner 2 editor
│ │ └── editor/ # Editor picks
│ ├── (public)/ # Public-facing interface
│ │ ├── layout.tsx # Public layout (header + footer)
│ │ ├── page.tsx # Home page
│ │ ├── home2/ # Alternative home
│ │ ├── about/ # About page
│ │ ├── all-blogs/ # Blog listing
│ │ ├── blog-details/[slug]/ # Blog post detail
│ │ ├── lets-talk/ # Contact page
│ │ └── terms/ # Terms & conditions
│ └── documentation/ # Documentation viewer route
│
├── actions/ # Next.js Server Actions
│ ├── blog/ # Blog mutations
│ ├── author/ # Author mutations
│ ├── categories/ # Category mutations
│ ├── settings/ # Settings mutations
│ ├── subscribe/ # Newsletter actions
│ ├── contactUs/ # Contact form actions
│ ├── adminLogin/ # Auth actions
│ └── profile/ # Profile actions
│
├── components/ # React components
│ ├── ui/ # Shadcn/Radix UI primitives (30+ components)
│ ├── features/ # Feature-specific components
│ │ └── landing/ # Public page sections
│ ├── form/ # Form field components
│ │ ├── InputField.tsx
│ │ ├── PasswordField.tsx
│ │ ├── FileUploadComponent.tsx
│ │ └── PhoneNumber.tsx
│ ├── custom/ # Custom business components
│ │ ├── FeedbackModal.tsx
│ │ ├── NewsletterModal.tsx
│ │ └── ContactUsForm.tsx
│ ├── shared/ # Shared utility components
│ ├── hooks/ # Component-level hooks
│ │ ├── useFilteredBlogPosts.tsx
│ │ └── useLoadMore.tsx
│ └── providers/ # React providers
│ └── theme-provider.tsx
│
├── config/ # Configuration modules
│ ├── database.ts # MongoDB connection + auto-seeding
│ ├── cloudinary.ts # Cloudinary upload/delete utilities
│ ├── routes.ts # Route constants
│ ├── constant.ts # App constants (roles, cache keys)
│ └── cache.ts # In-memory cache config
│
├── hooks/ # App-level custom hooks
│ └── use-mobile.ts
│
├── lib/ # Utility functions
│ ├── utils.ts # General utilities (cn, etc.)
│ ├── api-client.ts # HTTP client wrapper
│ ├── authenticate.ts # Auth helpers
│ ├── validation-schema.ts # Zod validation schemas
│ ├── types.ts # Shared TypeScript interfaces
│ ├── metadata.ts # SEO metadata builder
│ ├── date-format.ts # Date formatting helpers
│ ├── mongo-adapter.ts # MongoDB adapter
│ └── file-validator.ts # File type/size validation
│
├── model/ # Mongoose schemas & models
│ ├── Blog.ts # Blog schema
│ ├── Category.ts # Category schema
│ ├── User.ts # User/Author schema
│ ├── Settings.ts # App settings schema
│ ├── Contactus.ts # Contact submissions schema
│ ├── Feedback.ts # Feedback schema
│ ├── Subscribe.ts # Newsletter subscriber schema
│ └── HomeSection.ts # Home section data schema
│
├── provider/ # App-level providers
│ ├── AuthSessionProvider.tsx # NextAuth session provider
│ ├── DashboardProvider.tsx # Dashboard context
│ └── ThemeProvider.tsx # Theme context
│
├── public/ # Static assets
│ ├── fonts/ # Custom web fonts
│ ├── images/ # Static images
│ ├── sample-data/ # Sample data files
│ ├── documentation.html # Standalone documentation
│ └── documentation.pdf # PDF documentation
│
├── store/ # Zustand state stores
│ ├── useBlogStore.ts
│ ├── useUserProfile.ts
│ ├── useLoadingStore.ts
│ └── useOutletStore.ts
│
├── types/ # TypeScript type extensions
│ ├── next.d.ts
│ └── next-auth.d.ts
│
├── next.config.ts
├── tsconfig.json
├── postcss.config.mjs
├── components.json
└── package.json
Application Routes
Public Routes
| Route | Description |
|---|---|
/ | Home page — hero banners, featured posts, categories |
/home2 | Alternate home page layout |
/about | About the publication |
/all-blogs | Paginated blog listing with filters |
/blog-details/[slug] | Individual blog post with related posts |
/lets-talk | Contact form page |
/terms | Terms & conditions and privacy policy |
/documentation | Template documentation viewer |
Auth Routes
| Route | Description |
|---|---|
/admin/login | Admin login page |
Admin Dashboard Routes (Protected)
| Route | Description |
|---|---|
/admin/dashboard | Overview — stats and quick links |
/admin/dashboard/blogs | Blog list with search and filters |
/admin/dashboard/blogs/create | Create new blog post |
/admin/dashboard/blogs/create/[id] | Edit existing blog post |
/admin/dashboard/categories | Category list with drag-and-drop sort |
/admin/dashboard/author | Author management |
/admin/dashboard/settings | Global settings |
/admin/dashboard/contact-us | Inbox for contact form messages |
/admin/dashboard/feedback | User feedback submissions |
/admin/dashboard/subscribe | Newsletter subscriber list |
/admin/dashboard/banner-one | Homepage hero banner 1 editor |
/admin/dashboard/banner-two | Homepage hero banner 2 editor |
/admin/dashboard/editor | Editor's picks showcase editor |
Authentication
The template uses NextAuth.js 5.x (beta) for session management alongside a custom JWT-based API authentication layer.
How It Works
- Admin visits
/admin/loginand submits credentials. - NextAuth validates credentials against the MongoDB
userscollection. - A session is created server-side; a signed JWT is also issued for API requests.
- All
/api/admin/*routes verify the JWT from theAuthorization: Bearer <token>header. - Protected Next.js pages under
(private)redirect unauthenticated users to/admin/login.
Security Details
- Passwords hashed with bcrypt (10 salt rounds)
NEXTAUTH_SECRETandJWT_SECRETmust be strong, unique random strings- Default admin password must be changed on first login
- Role-based access:
admin= full dashboard access;user= author access only
Generating Secrets
openssl rand -base64 32
Run this command twice — once for NEXTAUTH_SECRET and once for JWT_SECRET.
State Management
The app uses Zustand for global client-side state.
| Store | File | Purpose |
|---|---|---|
useBlogStore | store/useBlogStore.ts | Blog list, filters, pagination state |
useUserProfile | store/useUserProfile.ts | Current authenticated admin profile |
useLoadingStore | store/useLoadingStore.ts | Global loading indicator state |
useOutletStore | store/useOutletStore.ts | Outlet / location data |
Image Management
All images are managed through Cloudinary CDN. Credentials are stored in MongoDB — no .env changes are needed for Cloudinary configuration.
Setup Steps
- Create a Cloudinary account at cloudinary.com.
- Copy your Cloud Name, API Key, and API Secret from the dashboard.
- In the admin dashboard, navigate to Settings → Cloudinary and enter your credentials.
- Set a Folder Name (e.g.,
blog-uploads) for organized media storage.
Supported Operations
| Operation | Description |
|---|---|
| Upload | Blog cover images, banners, author avatars, logo, favicon |
| Delete | Single asset by Cloudinary public ID |
| Bulk delete | Multiple assets at once |
| Folder delete | Delete all assets in a folder including the folder itself |
Server Actions accept file uploads up to 50 MB (configured in next.config.ts via serverActions.bodySizeLimit).
API — Authentication
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /api/admin/auth/login | Admin login — returns JWT | No |
| GET | /api/admin/auth/me | Get current admin profile | Yes |
| PATCH | /api/admin/auth/password | Change admin password | Yes |
| GET / POST | /api/auth/[...nextauth] | NextAuth.js session handler | — |
Login Request
POST /api/admin/auth/login
{
"email": "admin@example.com",
"password": "ChangeMe123!"
}
Login Response
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"_id": "...",
"name": "Admin",
"email": "admin@example.com",
"role": "admin"
}
}
API — Blogs
Admin (Protected)
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/admin/blog | List all blogs with pagination and filters |
| POST | /api/admin/blog | Create a new blog post |
| PUT | /api/admin/blog/[id] | Update a blog post by ID |
| DELETE | /api/admin/blog/[id] | Delete a blog post by ID |
| PATCH | /api/admin/blog/status/[id] | Toggle published / draft status |
Create Blog Request
POST /api/admin/blog
{
"title": "My Blog Post Title",
"slug": "my-blog-post-title",
"shortDescription": "A brief summary of the post",
"description": "<p>Full HTML content from the rich text editor</p>",
"readTime": "5 min read",
"image": "https://res.cloudinary.com/cloud/image/upload/v1/...",
"category": "60d5f9b0c9e77c001f3c9d34",
"createdBy": "60d5f9b0c9e77c001f3c9d35",
"isPopular": false,
"isFeature": true,
"status": true
}
Public
| Method | Endpoint | Query Params | Description |
|---|---|---|---|
| GET | /api/blog | page, limit, category | Paginated published blogs |
| GET | /api/blog/all | — | All published blogs |
| GET | /api/blog/trending | — | Trending blogs sorted by views |
| GET | /api/blog/more | page, limit | Load more blogs for infinite scroll |
| GET | /api/blog/[slug] | — | Single blog post by URL slug |
| PATCH | /api/blog/[slug]/views | — | Increment view counter |
| GET | /api/blog/[slug]/related | — | Related posts in the same category |
API — Categories
Admin (Protected)
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/admin/category | List all categories |
| POST | /api/admin/category | Create a category |
| PUT | /api/admin/category/[id] | Update a category |
| DELETE | /api/admin/category/[id] | Delete a category |
| PATCH | /api/admin/category/status/[id] | Toggle active / inactive |
| POST | /api/admin/category/sort | Reorder categories by drag-and-drop |
Sort Request
POST /api/admin/category/sort
{ "orderedIds": ["id1", "id2", "id3"] }
Public
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/category | All active categories |
API — Settings
Admin (Protected)
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/admin/settings | Get all settings |
| POST | /api/admin/settings/general | Update general info (name, logo, socials) |
| POST | /api/admin/settings/cloudinary | Update Cloudinary credentials |
| POST | /api/admin/settings/metadata | Update SEO metadata |
| POST | /api/admin/settings/page-banner | Update page banner images |
| POST | /api/admin/settings/business-hour | Update business hours |
| POST | /api/admin/settings/terms | Update terms & privacy policy |
Cloudinary Request Body
{
"cloudName": "your-cloud-name",
"apiKey": "123456789012345",
"apiSecret": "your-api-secret",
"folder": "blog-uploads",
"secureUrlBase": "https://res.cloudinary.com/your-cloud-name"
}
SEO Metadata Request Body
{
"title": "My Blog — Latest Articles",
"applicationName": "My Blog",
"description": "Explore insightful articles and stories.",
"keywords": ["blog", "articles", "news"],
"openGraphImage": "https://res.cloudinary.com/..."
}
Public
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/settings | Public settings (name, logo, social links) |
API — Contact & Feedback
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/contact-us | Submit a contact form message |
| GET | /api/admin/contact-us | List all contact messages |
| DELETE | /api/admin/contact-us | Delete contact messages |
| PUT | /api/admin/contact-us/[id] | Update message (mark as read) |
| POST | /api/feedback | Submit user feedback |
| GET | /api/admin/feedback | List all feedback entries |
| PATCH | /api/admin/feedback/[id] | Update feedback status |
| DELETE | /api/admin/feedback | Delete feedback entries |
API — Search
| Method | Endpoint | Query | Description |
|---|---|---|---|
| GET | /api/search | ?q=keyword | Full-text search across blog titles and content |
Example: GET /api/search?q=javascript
API — Menu, Dashboard & Cache
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/menu | Full navigation menu structure |
| GET | /api/menu/[slug] | Single menu item by slug |
| GET | /api/admin/dashboard | Aggregate stats for dashboard |
| POST | /api/cache | Invalidate in-memory cache |
Database — Blog Model
Collection: blogs
Indexes: status+slug, status+isPopular, status+isFeature, status+title, status+category
Database — Category Model
Database — User Model
Database — Settings Model
Database — Other Models
Contact Us
Feedback
Subscribe (Newsletter)
HomeSection
Admin — First Time Setup
- Login at
/admin/loginwith default credentials - Settings → General — set company name, logo, favicon, social links
- Settings → Cloudinary — configure Cloudinary CDN credentials
- Settings → SEO Metadata — set page title, description, Open Graph image
- Create at least one Category before creating blog posts
- Create at least one Author before creating blog posts
- Create your first Blog Post
Admin — Creating Blog Posts
- Navigate to Blogs → Create New
- Enter the Title — the slug auto-generates but can be customized
- Add a Short Description — this appears in blog listing cards
- Write full content in the SunEditor rich text editor
- Upload a Cover Image — uploaded directly to Cloudinary
- Select a Category and Author
- Set Read Time (e.g., "5 min read")
- Toggle
isFeatureto show on home page featured section - Toggle
isPopularto show in trending section - Click Publish for live or save as Draft
Admin — Managing Categories
- Categories can be reordered by drag-and-drop on the categories page
- Each category has an active/inactive toggle for visibility control
- Deleting a category does not delete its associated blog posts
- The
countfield on each category reflects the number of assigned blogs
Admin — Home Page Customization
Use the Showcase section in the admin sidebar to edit home page content:
| Section | Route | Description |
|---|---|---|
| Banner One | /admin/dashboard/banner-one | Primary hero banner — tagline, heading, description, image |
| Banner Two | /admin/dashboard/banner-two | Secondary hero banner — alternate layout |
| Editor Picks | /admin/dashboard/editor | Curated posts for the editor's picks section on home |
Admin — Settings Guide
| Tab | Description |
|---|---|
| General | Company name, contact info, logo, favicon, social media links |
| Cloudinary | CDN credentials — Cloud Name, API Key, API Secret, Folder |
| SEO Metadata | Page title, meta description, keywords, Open Graph image |
| Page Banners | Hero images for static internal pages |
| Business Hours | Operating hours per day of the week (0 = Sunday to 6 = Saturday) |
| Terms & Policy | Rich-text HTML content for Terms of Service and Privacy Policy pages |
| Security | Change admin account password |
Build & Production
Build
pnpm build
Uses Turbopack for fast production bundling (configured in package.json).
Start Production Server
pnpm start
Pre-Deploy Checklist
- ☐
MONGODB_URIpoints to production MongoDB cluster - ☐
NEXTAUTH_SECRETis a strong, unique random string - ☐
JWT_SECRETis a strong, unique random string - ☐
NEXTAUTH_URLis set to the production domain (e.g.,https://yourdomain.com) - ☐ Admin default password has been changed
- ☐ Cloudinary credentials are configured in admin settings
Deployment
Vercel (Recommended)
- Push project to GitHub, GitLab, or Bitbucket.
- Import at vercel.com.
- Set all environment variables in the Vercel project settings.
- Deploy — Vercel handles builds and CDN automatically.
Self-Hosted (Node.js + PM2)
pnpm build
pm2 start pnpm --name "blog-template" -- start
Run behind a reverse proxy (Nginx or Caddy) for SSL termination.
Docker
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm install -g pnpm && pnpm install && pnpm build
EXPOSE 3000
CMD ["pnpm", "start"]
Credits
| Tool | URL |
|---|---|
| Next.js | https://nextjs.org |
| React | https://react.dev |
| MongoDB | https://mongodb.com |
| Mongoose | https://mongoosejs.com |
| NextAuth.js | https://next-auth.js.org |
| Cloudinary | https://cloudinary.com |
| Tailwind CSS | https://tailwindcss.com |
| Shadcn/ui | https://ui.shadcn.com |
| Radix UI | https://radix-ui.com |
| Zustand | https://zustand-demo.pmnd.rs |
| React Hook Form | https://react-hook-form.com |
| Zod | https://zod.dev |
| SunEditor | https://github.com/JiHong88/SunEditor |
| DnD Kit | https://dndkit.com |
| TanStack Table | https://tanstack.com/table |
| Lucide Icons | https://lucide.dev |