A production-ready, full-stack newspaper & news publishing system built with Next.js 16, React 19, MongoDB, and Cloudinary. Ships with a polished public news portal, an admin dashboard, and an author workflow — all out of the box.
Template Newspaper is a complete news publishing platform that covers every layer of a modern media website. It ships with everything you need out of the box:
| Requirement | Minimum Version | Notes |
|---|---|---|
| Node.js | 18.18 or later | LTS recommended |
| npm | 9.x or later | pnpm / yarn / bun also supported |
| MongoDB | 6.0 or later | Local or MongoDB Atlas (free tier) |
| Cloudinary Account | Any | Free tier is sufficient |
unzip template-newspaper.zip -d template-newspaper
cd template-newspaper
npm install
cp .env.example .env
# Then edit .env with your actual values
See the Environment Variables section for all required values.
Insert an admin user document directly into MongoDB (via MongoDB Compass or Atlas UI). Set role to "admin" and use a bcrypt-hashed password.
npm run dev
Open http://localhost:3000 in your browser. The admin dashboard is at http://localhost:3000/admin/dashboard. The interactive documentation is at http://localhost:3000/documentation.
Create a .env file in the project root with the following variables:
# ─── Database ──────────────────────────────────────────────────────
# MongoDB connection string (Atlas or local instance)
MONGODB_URI=mongodb+srv://<user>:<password>@cluster.mongodb.net/<db>
# ─── Authentication ────────────────────────────────────────────────
# Long random string for signing JWTs. Generate: openssl rand -base64 32
NEXTAUTH_SECRET=your-secret-key-here
# ─── Internal API URL ──────────────────────────────────────────────
# Use http://localhost:3000 for local dev; production domain in prod
NEXT_API_URL=http://localhost:3000
# ─── Cloudinary ────────────────────────────────────────────────────
# Can also be configured via Admin → Settings → Cloudinary
CLOUDINARY_CLOUD_NAME=your-cloud-name
CLOUDINARY_API_KEY=your-api-key
CLOUDINARY_API_SECRET=your-api-secret
CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY, and CLOUDINARY_API_SECRET are also manageable through Admin Dashboard → Settings → Cloudinary (saved in the database). Changes take effect without redeploying.
.env to version control. It is listed in .gitignore by default. Always set a strong, unique NEXTAUTH_SECRET in production.
| Command | Description |
|---|---|
npm run dev | Start development server with Turbopack at localhost:3000 |
npm run build | Create an optimised production build |
npm run start | Serve the production build |
npm run lint | Run ESLint code quality checks |
template-newspapper/
├── public/
│ ├── documentation.html # Standalone HTML documentation
│ └── documentation-pdf.html # Print-optimised documentation
│
├── src/
│ ├── app/ # Next.js App Router root
│ │ ├── (auth)/ # Auth pages (no header/footer)
│ │ │ └── login/
│ │ │
│ │ ├── (public)/ # Public-facing pages
│ │ │ ├── layout.tsx # Header + Footer wrapper
│ │ │ ├── page.tsx # Homepage (classic / modern)
│ │ │ ├── [category]/ # Category listing pages
│ │ │ ├── news/[slug]/ # News detail page
│ │ │ ├── about-us/
│ │ │ ├── contact-us/
│ │ │ ├── privacy-policy/
│ │ │ ├── terms-and-conditions/
│ │ │ └── site-map/
│ │ │
│ │ ├── (dashboard)/ # Protected dashboard (auth required)
│ │ │ ├── layout.tsx # Sidebar shell
│ │ │ ├── admin/
│ │ │ │ ├── dashboard/ # Stats & charts
│ │ │ │ ├── news/ # News CRUD & status management
│ │ │ │ ├── categories/ # Category & subcategory management
│ │ │ │ ├── authors/ # Author account management
│ │ │ │ ├── contacts/ # Contact submissions
│ │ │ │ ├── newsletters/ # Newsletter subscribers
│ │ │ │ └── settings/ # Site-wide settings
│ │ │ └── author/
│ │ │ ├── dashboard/ # Author personal stats
│ │ │ └── news/ # Author's own articles
│ │ │
│ │ ├── documentation/ # /documentation route
│ │ │
│ │ └── api/
│ │ ├── auth/[...nextauth]/ # NextAuth handler
│ │ ├── admin/ # All admin API endpoints
│ │ ├── author/ # Author API endpoints
│ │ └── landing/ # Public API endpoints
│ │
│ ├── components/
│ │ ├── ui/ # shadcn/ui primitives (50+ components)
│ │ ├── form/ # Reusable form field components
│ │ ├── shared/ # Header, Footer, layout components
│ │ ├── dashboard/ # Dashboard-specific widgets
│ │ └── data-table/ # TanStack React Table components
│ │
│ ├── lib/
│ │ ├── async-handler.ts # API route wrapper (auth + validation)
│ │ ├── authenticate.ts # JWT verification middleware
│ │ ├── fetcher.ts # Authenticated HTTP client
│ │ ├── routes.ts # Centralised route constants
│ │ ├── utils.ts # Helpers (cn, slugify, paginate…)
│ │ └── validation-schema.ts # Zod schemas for all entities
│ │
│ ├── model/
│ │ ├── User.ts
│ │ ├── News.ts
│ │ ├── Category.ts
│ │ ├── Contact.ts
│ │ ├── Newsletter.ts
│ │ └── Settings.ts
│ │
│ ├── services/ # Business logic / server actions
│ │ └── auth.ts / news.ts / categories.ts / authors.ts / …
│ │
│ ├── config/
│ │ ├── database.ts # MongoDB connection singleton
│ │ ├── cloudinary.ts # Cloudinary SDK setup
│ │ └── constant.ts # App-wide constants (roles, statuses)
│ │
│ ├── hooks/
│ │ ├── use-user.ts
│ │ └── use-mobile.ts
│ │
│ └── store/ # Zustand client state stores
│ ├── user-store.ts
│ ├── breadcrumb-store.ts
│ ├── category-store.tsx
│ └── table-store.ts
│
├── .env / .env.example
├── next.config.ts
├── tailwind.config.ts
├── tsconfig.json
├── components.json # shadcn/ui config
└── package.json
| Route | Description |
|---|---|
/ | Homepage — Classic or Modern layout (configured in settings) |
/[category] | Category listing page with subcategory navigation and pagination |
/news/[slug] | Full news article detail page |
/about-us | About Us page (content editable from admin) |
/contact-us | Contact form page |
/privacy-policy | Privacy Policy page (content editable from admin) |
/terms-and-conditions | Terms & Conditions page (content editable from admin) |
/site-map | Auto-generated sitemap |
/login | Login page for admin and author accounts |
/documentation | Built-in interactive documentation |
All admin routes are under /admin/ and require a valid admin authentication session.
| Route | Description |
|---|---|
/admin/dashboard | Overview statistics and charts |
/admin/news | News list with filters and status management |
/admin/news/create | Create new article |
/admin/news/[slug] | Edit existing article |
/admin/categories | Category list with drag-to-reorder |
/admin/categories/create | Create new category |
/admin/categories/[slug] | Edit category & subcategories |
/admin/authors | Author account management |
/admin/contacts | Contact form submissions |
/admin/newsletters | Newsletter subscriber list |
/admin/settings/general | General site settings |
/admin/settings/cloudinary | Cloudinary credentials |
/admin/settings/metadata | SEO metadata settings |
/admin/settings/terms-and-conditions | Terms page content |
/admin/settings/privacy-policy | Privacy policy content |
/admin/settings/about-us | About Us page content |
All author routes are under /author/ and require a valid author session.
| Route | Description |
|---|---|
/author/dashboard | Author personal statistics |
/author/news | Author's own article list |
/author/news/create | Create new article |
/author/news/[slug] | Edit own article |
| Role | Access |
|---|---|
admin | Full access to admin dashboard, all content, settings, and user management |
author | Access to author dashboard; can create, edit, and submit own articles only |
user | Reserved for future public-user features; no dashboard access currently |
Route protection is enforced at middleware level — unauthenticated requests to dashboard routes are redirected to /login.
Authentication is powered by NextAuth v5 with the Credentials provider and JWT sessions (30-day expiry).
/login.Authorization: Bearer <token>.authenticate() middleware verifies the token and attaches the user to the request context.| File | Purpose |
|---|---|
src/app/api/auth/[...nextauth]/auth.ts | NextAuth configuration and callbacks |
src/lib/authenticate.ts | JWT verification middleware for API routes |
src/lib/async-handler.ts | API route wrapper with role-checking and Zod validation |
src/hooks/use-user.ts | React hook to read the current authenticated user |
All API responses follow a consistent envelope format:
// Success
{ "success": true, "statusCode": 200, "message": "...", "data": { ... } }
// Error
{ "success": false, "statusCode": 400, "message": "...", "errors": [ ... ] }
latest.page (default: 1), limit (default: 10)viewCount.{ "name": "Jane Doe", "email": "jane@example.com", "message": "Hello!" }
{ "email": "reader@example.com" }
Authorization: Bearer <token>
// Request
{ "email": "admin@example.com", "password": "your_password" }
// Response
{
"success": true,
"data": {
"token": "eyJhbGci...",
"user": { "name": "Admin", "email": "admin@example.com", "role": "admin" }
}
}
{ "oldPassword": "current", "newPassword": "new_pass", "confirmPassword": "new_pass" }
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/admin/category | List all categories with subcategories |
| POST | /api/admin/category | Create a new category |
| GET | /api/admin/category/[slug] | Get single category with its subcategories |
| PUT | /api/admin/category/[slug] | Update category details and subcategories |
| DELETE | /api/admin/category/[slug] | Delete category and its subcategories |
| PUT | /api/admin/category/sort | Reorder categories (drag-and-drop) |
// Create Category — Request Body
{
"name": "Politics",
"slug": "politics",
"status": true,
"featured": true,
"subCategories": [
{ "name": "National", "slug": "national", "status": true }
]
}
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/admin/news | Paginated news list with status, category, and author filters |
| POST | /api/admin/news | Create and publish a news article (with image upload) |
| GET | /api/admin/news/[slug] | Get a single article |
| PUT | /api/admin/news/[slug] | Update article content, status, or flags |
| DELETE | /api/admin/news/[slug] | Soft-delete article (recoverable) |
| PUT | /api/admin/news/sort | Reorder articles by position |
| PUT | /api/admin/news/reject/[slug] | Reject a submitted article with a reason |
// News article status values
"draft" // saved but not submitted
"review" // submitted by author, awaiting admin action
"published" // live on the public portal
"rejected" // rejected by admin with a reason
// Reject endpoint — Request Body
{ "rejectReason": "Content needs more sources." }
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/admin/contact | List all contact form submissions |
| PUT | /api/admin/contact/[id] | Toggle the replied status of a contact submission |
| GET | /api/admin/newsletter | List all newsletter subscribers |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/admin/settings/general | Get general site settings |
| PUT | /api/admin/settings/general | Update general settings (name, logo, social links, home view) |
| GET | /api/admin/settings/cloudinary | Get Cloudinary configuration |
| PUT | /api/admin/settings/cloudinary | Update Cloudinary credentials |
| GET | /api/admin/settings/metadata | Get SEO metadata settings |
| PUT | /api/admin/settings/metadata | Update SEO metadata (title, description, keywords, OG image) |
| GET | /api/admin/settings/terms | Get Terms & Privacy HTML content |
| PUT | /api/admin/settings/terms | Update Terms, Privacy Policy, and About Us content |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/admin/dashboard | Returns overview stats, chart data, and recent articles for the admin |
| GET | /api/author/dashboard | Returns the author's personal stats (article counts by status) |
| Field | Type | Description |
|---|---|---|
name | String | Display name |
email | String (unique) | Login email address |
password | String | Bcrypt-hashed password |
role | Enum | admin, author, or user |
image | String | Profile image URL (Cloudinary) |
status | Boolean | Account active / inactive flag |
softDelete | Boolean | Soft-deletion flag (default: false) |
| Field | Type | Description |
|---|---|---|
title | String | Article headline |
slug | String (unique) | URL-friendly identifier (auto-generated) |
image | String | Featured image URL (Cloudinary) |
shortDesc | String | Summary / excerpt for listing pages |
body | String | Full HTML article content from SunEditor |
readTime | Number | Estimated reading time in minutes |
author | ObjectId → User | Reference to the article's author |
category | ObjectId → Category | Primary category reference |
subCategory | ObjectId → Category | Subcategory reference |
status | Enum | draft | review | published | rejected |
rejectReason | String | Rejection reason provided by admin |
publishedAt | Date | Timestamp when article was published |
viewCount | Number | Incremented on each public view |
breakingNews | Boolean | Show in breaking news ticker |
latest | Boolean | Show in latest news section |
featured | Boolean | Show in featured section |
position | Number | Manual sort order |
softDelete | Boolean | Soft-deletion flag |
| Field | Type | Description |
|---|---|---|
name | String | Category display name |
slug | String (unique) | URL slug |
parent | ObjectId → Category | null for top-level; parent ID for subcategories |
status | Boolean | Active / inactive |
featured | Boolean | Show in featured navigation |
position | Number | Manual sort order |
| Namespace | Key Fields |
|---|---|
general | companyName, phone, address, logo, favicon, social links, homeView (classic/modern) |
cloudinary | cloudName, apiKey, apiSecret, folder, secureUrlBase |
metadata | title, applicationName, description, keywords, openGraphImage |
termsPolicy | terms, policy, aboutUs (rich HTML content) |
| Model | Field | Type | Description |
|---|---|---|---|
| Contact | name | String | Submitter's name |
email | String | Submitter's email | |
message | String | Message body | |
isReplied | Boolean | Replied status flag | |
| Newsletter | email | String (unique) | Subscriber email address |
Switch between Classic and Modern homepage layouts from Admin → Settings → General → Home View. The change takes effect immediately without a rebuild.
Configure from Admin → Settings → Cloudinary. Required fields:
newspaper)https://res.cloudinary.comGo to Admin → Settings → Metadata to set the page title template, default description, keywords, and Open Graph / Twitter Card image.
The About Us, Privacy Policy, and Terms & Conditions page content is fully editable from Admin → Settings using the rich text editor. No code changes required.
# Build the production bundle
npm run build
# Start the production server
npm run start
Create a Dockerfile based on the official Next.js Docker example and pass environment variables at runtime via --env-file .env.
NEXTAUTH_SECRET (use openssl rand -base64 32)NEXT_API_URL to your production domain (e.g. https://yourdomain.com)If you have questions, issues, or feature requests, please open a support ticket through your purchase platform's messaging system.
When reporting a bug, please include:
node -v)Documentation last updated: April 2026