MERN Stack8 min read

Building a MERN App with Next.js: Complete Guide

Learn how to build a full-stack MERN application using Next.js as the frontend framework — from setup to deployment, including performance and SEO practices.

01

Introduction

The MERN stack (MongoDB, Express.js, React, Node.js) is one of the most popular full-stack combinations. Replacing a plain React SPA with Next.js adds SSR/SSG, routing, and built-in optimization.

This guide walks through a task management app that covers authentication, CRUD, and a practical deploy path.

02

Setup & architecture

Two parts: a Next.js frontend and a Node/Express API. The frontend owns UI and routing; the backend owns auth, data, and API endpoints.

Frontend

npx create-next-app@latest task-manager-frontend
cd task-manager-frontend
npm install axios @tanstack/react-query

Backend

mkdir task-manager-backend
cd task-manager-backend
npm init -y
npm install express mongoose cors dotenv bcryptjs jsonwebtoken
npm install -D nodemon
03

MongoDB models

Two collections — Users and Tasks — keep ownership clear while staying simple to query.

const userSchema = new mongoose.Schema({
  username: { type: String, required: true, unique: true },
  email: { type: String, required: true, unique: true },
  password: { type: String, required: true },
  tasks: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Task' }]
}, { timestamps: true });
const taskSchema = new mongoose.Schema({
  title: { type: String, required: true },
  description: String,
  completed: { type: Boolean, default: false },
  priority: {
    type: String,
    enum: ['low', 'medium', 'high'],
    default: 'medium'
  },
  dueDate: Date,
  user: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    required: true
  }
}, { timestamps: true });
04

Express API

REST endpoints for auth and tasks, with JWT middleware and clear error responses.

const authenticateToken = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];
  if (!token) {
    return res.status(401).json({ message: 'Access token required' });
  }
  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) return res.status(403).json({ message: 'Invalid token' });
    req.user = user;
    next();
  });
};
05

Next.js frontend

App Router for structure, React Query for server state, and a mix of server/client components where each fits.

const useTasks = () => {
  return useQuery({
    queryKey: ['tasks'],
    queryFn: async () => {
      const response = await fetch('/api/tasks', {
        headers: { Authorization: `Bearer ${getToken()}` }
      });
      return response.json();
    }
  });
};
06

Performance & SEO

  • Prefer Next.js Image for media
  • Ship solid meta tags and Open Graph
  • Dynamic-import heavy client modules
  • Cache API responses thoughtfully
  • Keep bundles lean with tree-shaking
07

Deploy

Common split: Vercel for Next.js, a Node host for the API, and MongoDB Atlas for data.

# Frontend (.env.local)
NEXT_PUBLIC_API_URL=https://your-api-domain.com

# Backend (.env)
MONGODB_URI=mongodb+srv://...
JWT_SECRET=your-super-secret-jwt-key
PORT=5000
08

Wrap-up

MERN + Next.js is a strong default for apps that need a real API and a fast, SEO-friendly frontend. Extend with sockets, uploads, or OAuth when the product needs them.