Build a complete blog platform with the MERN stack in 2025 — MongoDB, Express, React, and Node.js with authentication, rich text editing, and deployment.

Abdur Razzak
Full-Stack Web Developer
In this tutorial we will build a full-featured blog platform: users can register, create blog posts with a rich text editor, upload cover images, and publish to a public listing page. The backend is Node.js + Express + MongoDB. The frontend is React with React Query for data fetching. We will deploy the API to Render and the frontend to Vercel.
Initialize with npm init -y and install: express, mongoose, dotenv, cors, bcryptjs, jsonwebtoken, multer (for file uploads), and cloudinary (for image storage). Create a modular structure: src/modules/blog/{blog.model.js, blog.controller.js, blog.route.js}. Define a Mongoose Blog schema with title, slug (auto-generated), content, coverImage, author (ref to User), tags, and publishedAt.
Create a User model with hashed passwords using bcryptjs. POST /api/auth/register creates a user. POST /api/auth/login returns a signed JWT (expires in 7d). An authMiddleware verifies the JWT on protected routes. Store the token in an httpOnly cookie or localStorage — for a blog platform, localStorage is acceptable since the admin is a single trusted user.
Configure multer to accept image uploads in memory (memoryStorage). In your blog creation route, upload the buffer to Cloudinary using cloudinary.uploader.upload_stream(). Store the returned secure_url in the blog document. This approach avoids storing images on your server disk and gives you automatic CDN delivery, transformations, and format optimization (auto WebP for modern browsers).
Set up a React app with Vite and install: react-router-dom, @tanstack/react-query, axios, and a rich text editor (Tiptap or React Quill). Use React Query's useQuery to fetch and cache the blog list. Use useMutation for creating and updating posts. React Query automatically handles loading/error states and cache invalidation after mutations — no manual state management needed.
Push your backend to GitHub and connect it to Render (free tier). Set environment variables: MONGODB_URI (from MongoDB Atlas), JWT_SECRET, CLOUDINARY_URL. Push the React frontend to Vercel. Update your backend's CORS config to allow your Vercel domain. Update the React app's API base URL in a .env file. Verify the full flow: register → login → create post → see it on the public listing.