A feature-complete Instagram clone built with the MERN Stack (MongoDB, Express.js, React.js, Node.js), supporting all core Instagram features plus an InstaClone Premium subscription to enjoy an ad-free experience.
InstaClone is a production-grade social media platform replicating the core Instagram experience β from photo/video sharing and stories to real-time messaging and explore feeds. Users who want a clean, distraction-free experience can subscribe to InstaClone Premium to remove all ads from their feed, stories, reels, and explore sections.
| Technology | Purpose | |β|β| | React.js 18 | UI framework | | Redux Toolkit | Global state management | | React Router v6 | Client-side routing | | Tailwind CSS | Utility-first styling | | Socket.IO Client | Real-time communication | | Axios | HTTP client | | React Query | Server state & caching | | Framer Motion | Animations | | HLS.js | Video streaming (Reels) |
| Technology | Purpose | |β|β| | Node.js | Runtime environment | | Express.js | REST API framework | | MongoDB + Mongoose | Database & ODM | | Socket.IO | WebSocket server | | JWT | Authentication tokens | | Bcrypt.js | Password hashing | | Multer | File upload middleware | | Cloudinary | Media storage & CDN | | Redis | Session caching & pub/sub | | Bull | Background job queue (notifications, emails) |
| Service | Purpose | |β|β| | Cloudinary | Image & video hosting | | Razorpay / Stripe | Premium subscription payments | | Nodemailer + SendGrid | Email verification, OTP | | Firebase (optional) | Push notifications | | Google OAuth 2.0 | Social login |
Users can subscribe to InstaClone Premium to get a completely ad-free experience across the entire platform.
| Surface | Free Tier | Premium | |β|β|β| | Feed sponsored posts | Every 5th post | None | | Story ads | Every 3β4 stories | None | | Reel ads | Between scrolls | None | | Explore ads | In grid | None |
| Plan | Price | Billing | |β|β|β| | Monthly | βΉ89 / $1.99 | Monthly auto-renewal | | Yearly | βΉ699 / $14.99 | Billed annually (save ~35%) |
isPremium: true and premiumExpiry: Date are set on the User documentpremiumBadge field toggles the gold checkmark on their profile (optional cosmetic)isPremium on every feed / story / reel / explore request and strips ad-injection logic for premium userspremiumExpiry dateUser Clicks Subscribe
β
βΌ
POST /api/premium/create-order
(Razorpay order or Stripe PaymentIntent created)
β
βΌ
Client renders payment modal
(Razorpay Checkout SDK / Stripe Elements)
β
βΌ
User completes payment
β
βΌ
Webhook received at POST /api/premium/webhook
(Razorpay signature verified / Stripe event verified)
β
βΌ
User.isPremium = true
User.premiumExpiry = now + plan duration
User.premiumPlan = "monthly" | "yearly"
β
βΌ
All feed APIs check req.user.isPremium
β true β skip ad injection
β false β inject ad every N posts/stories/reels
instaclone/
βββ client/ # React frontend
β βββ public/
β β βββ index.html
β βββ src/
β β βββ assets/ # Images, icons, fonts
β β βββ components/
β β β βββ common/ # Button, Modal, Avatar, Loader, etc.
β β β βββ feed/ # FeedPost, FeedAd, FeedSkeleton
β β β βββ story/ # StoryRing, StoryViewer, StoryCreator
β β β βββ reels/ # ReelCard, ReelPlayer, ReelUpload
β β β βββ explore/ # ExploreGrid, ExploreSearch
β β β βββ profile/ # ProfileHeader, PostGrid, Highlights
β β β βββ messages/ # ChatList, ChatWindow, MessageBubble
β β β βββ notifications/ # NotificationItem, NotificationPanel
β β β βββ premium/ # PremiumModal, PlanCard, PremiumBadge
β β βββ pages/
β β β βββ Home.jsx
β β β βββ Explore.jsx
β β β βββ Reels.jsx
β β β βββ Profile.jsx
β β β βββ Messages.jsx
β β β βββ Notifications.jsx
β β β βββ Login.jsx
β β β βββ Register.jsx
β β β βββ Settings.jsx
β β β βββ Premium.jsx
β β βββ redux/
β β β βββ store.js
β β β βββ slices/
β β β βββ authSlice.js
β β β βββ feedSlice.js
β β β βββ storySlice.js
β β β βββ reelsSlice.js
β β β βββ messageSlice.js
β β β βββ notificationSlice.js
β β β βββ premiumSlice.js
β β βββ hooks/ # useInfiniteScroll, useSocket, useMediaUpload
β β βββ services/ # axios instance, api calls
β β βββ utils/ # formatTime, filterImage, truncate, etc.
β β βββ socket/ # socket.js (Socket.IO client setup)
β β βββ App.jsx
β β βββ main.jsx
β βββ .env
β βββ tailwind.config.js
β βββ package.json
β
βββ server/ # Express backend
β βββ config/
β β βββ db.js # MongoDB connection
β β βββ cloudinary.js # Cloudinary setup
β β βββ redis.js # Redis connection
β β βββ razorpay.js # Razorpay / Stripe init
β βββ controllers/
β β βββ auth.controller.js
β β βββ user.controller.js
β β βββ post.controller.js
β β βββ story.controller.js
β β βββ reel.controller.js
β β βββ comment.controller.js
β β βββ message.controller.js
β β βββ notification.controller.js
β β βββ explore.controller.js
β β βββ premium.controller.js
β β βββ admin.controller.js
β βββ models/
β β βββ User.model.js
β β βββ Post.model.js
β β βββ Story.model.js
β β βββ Reel.model.js
β β βββ Comment.model.js
β β βββ Message.model.js
β β βββ Conversation.model.js
β β βββ Notification.model.js
β β βββ Ad.model.js
β β βββ PremiumTransaction.model.js
β βββ routes/
β β βββ auth.routes.js
β β βββ user.routes.js
β β βββ post.routes.js
β β βββ story.routes.js
β β βββ reel.routes.js
β β βββ comment.routes.js
β β βββ message.routes.js
β β βββ notification.routes.js
β β βββ explore.routes.js
β β βββ premium.routes.js
β β βββ admin.routes.js
β βββ middlewares/
β β βββ auth.middleware.js # verifyToken, optionalAuth
β β βββ premium.middleware.js # checkPremium, injectAds
β β βββ upload.middleware.js # multer config
β β βββ rateLimit.middleware.js # express-rate-limit
β β βββ error.middleware.js # global error handler
β βββ socket/
β β βββ socket.js # Socket.IO event handlers
β βββ jobs/
β β βββ expireStories.job.js # Bull job β delete 24h stories
β β βββ expirePremium.job.js # Bull job β expire subscriptions
β β βββ sendEmailDigest.job.js # Bull job β daily email activity
β βββ utils/
β β βββ generateToken.js
β β βββ sendEmail.js
β β βββ uploadToCloudinary.js
β β βββ feedAlgorithm.js
β β βββ adInjector.js
β βββ .env
β βββ app.js
β βββ server.js
β βββ package.json
β
βββ .gitignore
βββ docker-compose.yml # Optional: containerized dev setup
βββ README.md
server/.env)# App
NODE_ENV=development
PORT=5000
CLIENT_URL=http://localhost:3000
# MongoDB
MONGO_URI=mongodb+srv://<user>:<password>@cluster.mongodb.net/instaclone
# JWT
JWT_SECRET=your_jwt_secret_key
JWT_REFRESH_SECRET=your_refresh_secret_key
JWT_EXPIRES_IN=15m
JWT_REFRESH_EXPIRES_IN=7d
# Cloudinary
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
# Redis
REDIS_URL=redis://localhost:6379
# Google OAuth
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
# SendGrid / Nodemailer
EMAIL_HOST=smtp.sendgrid.net
EMAIL_PORT=587
EMAIL_USER=apikey
EMAIL_PASS=your_sendgrid_api_key
EMAIL_FROM=no-reply@instaclone.com
# Razorpay (India)
RAZORPAY_KEY_ID=your_razorpay_key
RAZORPAY_KEY_SECRET=your_razorpay_secret
RAZORPAY_WEBHOOK_SECRET=your_webhook_secret
# Stripe (International)
STRIPE_SECRET_KEY=sk_live_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
# Premium Plans (price in paise for Razorpay, cents for Stripe)
PREMIUM_MONTHLY_PRICE_INR=8900
PREMIUM_YEARLY_PRICE_INR=69900
PREMIUM_MONTHLY_PRICE_USD=199
PREMIUM_YEARLY_PRICE_USD=1499
client/.env)VITE_API_BASE_URL=http://localhost:5000/api
VITE_SOCKET_URL=http://localhost:5000
VITE_RAZORPAY_KEY_ID=your_razorpay_key_id
VITE_STRIPE_PUBLISHABLE_KEY=pk_live_xxx
VITE_GOOGLE_CLIENT_ID=your_google_client_id
git clone https://github.com/yourusername/instaclone.git
cd instaclone
# Install server dependencies
cd server
npm install
# Install client dependencies
cd ../client
npm install
Copy the example env files and fill in your credentials:
cp server/.env.example server/.env
cp client/.env.example client/.env
cd server
npm run seed
This seeds the database with:
Development mode (both client and server):
# From root directory (if concurrently is set up)
npm run dev
# Or run separately:
# Terminal 1 β Backend
cd server && npm run dev
# Terminal 2 β Frontend
cd client && npm run dev
Production build:
# Build frontend
cd client && npm run build
# Start backend (serves frontend static build)
cd server && npm start
The app will be available at:
http://localhost:3000http://localhost:5000/apihttp://localhost:5000/api/auth| Method | Endpoint | Description | Auth |
|β|β|β|β|
| POST | /register | Register new user | β |
| POST | /login | Login user | β |
| POST | /logout | Logout user | β
|
| POST | /refresh-token | Refresh JWT | β |
| POST | /google | Google OAuth login | β |
| POST | /forgot-password | Send OTP to email | β |
| POST | /reset-password | Reset password with OTP | β |
| POST | /verify-email | Verify email with token | β |
/api/users| Method | Endpoint | Description | Auth |
|β|β|β|β|
| GET | /:username | Get user profile | β
|
| PUT | /me | Update own profile | β
|
| POST | /:id/follow | Follow / Unfollow user | β
|
| GET | /:id/followers | Get followers list | β
|
| GET | /:id/following | Get following list | β
|
| POST | /:id/block | Block / Unblock user | β
|
| POST | /:id/restrict | Restrict / Unrestrict user | β
|
| GET | /suggestions | Get suggested users | β
|
| GET | /search?q= | Search users | β
|
| DELETE | /me | Delete account | β
|
/api/posts| Method | Endpoint | Description | Auth |
|β|β|β|β|
| POST | / | Create new post | β
|
| GET | /feed | Get home feed (with/without ads) | β
|
| GET | /:id | Get single post | β
|
| PUT | /:id | Edit post caption/tags | β
|
| DELETE | /:id | Delete post | β
|
| POST | /:id/like | Like / Unlike post | β
|
| POST | /:id/save | Save / Unsave post | β
|
| GET | /:id/likes | Get post likers | β
|
| GET | /user/:userId | Get userβs posts | β
|
/api/stories| Method | Endpoint | Description | Auth |
|β|β|β|β|
| POST | / | Upload new story | β
|
| GET | /feed | Get stories feed | β
|
| GET | /:userId | Get userβs active stories | β
|
| POST | /:id/view | Mark story as viewed | β
|
| POST | /:id/react | React to story | β
|
| DELETE | /:id | Delete story | β
|
/api/reels| Method | Endpoint | Description | Auth |
|β|β|β|β|
| POST | / | Upload new reel | β
|
| GET | /feed | Get reels feed (with/without ads) | β
|
| GET | /:id | Get single reel | β
|
| POST | /:id/like | Like / Unlike reel | β
|
| DELETE | /:id | Delete reel | β
|
/api/comments| Method | Endpoint | Description | Auth |
|β|β|β|β|
| POST | /post/:postId | Add comment to post | β
|
| GET | /post/:postId | Get comments for post | β
|
| PUT | /:id | Edit comment | β
|
| DELETE | /:id | Delete comment | β
|
| POST | /:id/like | Like / Unlike comment | β
|
| POST | /:id/reply | Reply to comment | β
|
| POST | /:id/pin | Pin comment (author only) | β
|
/api/messages| Method | Endpoint | Description | Auth |
|β|β|β|β|
| GET | /conversations | Get all conversations | β
|
| POST | /conversations | Create new conversation | β
|
| GET | /conversations/:id | Get messages in conversation | β
|
| POST | /conversations/:id | Send message | β
|
| DELETE | /messages/:id | Delete message | β
|
/api/premium| Method | Endpoint | Description | Auth |
|β|β|β|β|
| GET | /plans | Get available plans | β |
| POST | /create-order | Create payment order | β
|
| POST | /webhook | Handle payment webhook | β |
| GET | /status | Get userβs premium status | β
|
| POST | /cancel | Cancel subscription | β
|
/api/explore| Method | Endpoint | Description | Auth |
|β|β|β|β|
| GET | / | Get explore feed (with/without ads) | β
|
| GET | /trending | Get trending hashtags | β
|
| GET | /hashtag/:tag | Get posts by hashtag | β
|
| GET | /location/:id | Get posts by location | β
|
| GET | /search?q= | Search everything | β
|
{
username: { type: String, unique: true, required: true },
email: { type: String, unique: true, required: true },
password: { type: String }, // null for OAuth users
fullName: String,
bio: String,
website: String,
avatar: String, // Cloudinary URL
isPrivate: { type: Boolean, default: false },
isVerified: { type: Boolean, default: false }, // email verified
followers: [{ type: ObjectId, ref: 'User' }],
following: [{ type: ObjectId, ref: 'User' }],
blockedUsers: [{ type: ObjectId, ref: 'User' }],
restrictedUsers: [{ type: ObjectId, ref: 'User' }],
closeFriends: [{ type: ObjectId, ref: 'User' }],
savedPosts: [{ type: ObjectId, ref: 'Post' }],
followRequests: [{ type: ObjectId, ref: 'User' }],
// Premium
isPremium: { type: Boolean, default: false },
premiumPlan: { type: String, enum: ['monthly', 'yearly'], default: null },
premiumExpiry: Date,
premiumBadge: { type: Boolean, default: false },
// Auth
googleId: String,
refreshToken: String,
twoFASecret: String,
twoFAEnabled: { type: Boolean, default: false },
// Settings
hidelikeCounts: { type: Boolean, default: false },
notifications: { likes: Boolean, comments: Boolean, follows: Boolean, ... },
createdAt: Date,
updatedAt: Date
}
{
author: { type: ObjectId, ref: 'User', required: true },
media: [{ url: String, type: { type: String, enum: ['image','video'] }, publicId: String }],
caption: String,
hashtags: [String],
mentions: [{ type: ObjectId, ref: 'User' }],
tagged: [{ user: ObjectId, x: Number, y: Number }],
location: { name: String, lat: Number, lng: Number },
likes: [{ type: ObjectId, ref: 'User' }],
comments: [{ type: ObjectId, ref: 'Comment' }],
views: { type: Number, default: 0 },
commentsDisabled: { type: Boolean, default: false },
createdAt: Date
}
{
author: { type: ObjectId, ref: 'User', required: true },
media: { url: String, type: { type: String, enum: ['image','video'] }, publicId: String },
text: String,
stickers: Array,
music: { title: String, artist: String, url: String },
viewers: [{ user: ObjectId, viewedAt: Date }],
reactions:[{ user: ObjectId, emoji: String }],
audience: { type: String, enum: ['public', 'closeFriends'], default: 'public' },
expiresAt:{ type: Date, index: { expires: 0 } }, // TTL index
createdAt: Date
}
{
user: { type: ObjectId, ref: 'User', required: true },
orderId: String, // Razorpay / Stripe order ID
paymentId: String, // Razorpay / Stripe payment ID
plan: { type: String, enum: ['monthly', 'yearly'] },
amount: Number,
currency: String,
status: { type: String, enum: ['pending','success','failed','refunded'] },
gateway: { type: String, enum: ['razorpay', 'stripe'] },
createdAt: Date
}
destroy() called to remove media assetsAll real-time functionality is handled via Socket.IO with Redis adapter for horizontal scaling.
| Event | Direction | Description |
|---|---|---|
message:new |
Server β Client | New DM received |
message:seen |
Client β Server | Mark messages as seen |
typing:start |
Client β Server | User started typing |
typing:stop |
Client β Server | User stopped typing |
notification:new |
Server β Client | Push new notification |
story:new |
Server β Client | Notify followers of new story |
user:online |
Client β Server | User came online |
user:offline |
Client β Server | User went offline |
post:liked |
Server β Client | Real-time like count update |
Ads are managed by the Admin Panel and stored in the Ad collection.
// middlewares/premium.middleware.js
export const injectAds = async (req, res, next) => {
if (req.user?.isPremium) {
req.showAds = false;
} else {
req.showAds = true;
}
next();
};
// utils/adInjector.js
export const injectAdsInFeed = (posts, ads, frequency = 5) => {
if (!ads.length) return posts;
const result = [...posts];
let adIndex = 0;
for (let i = frequency; i < result.length; i += frequency + 1) {
result.splice(i, 0, { ...ads[adIndex % ads.length], isAd: true });
adIndex++;
}
return result;
};
{
title: String,
imageUrl: String,
linkUrl: String,
advertiser: String,
targetAudience: { ageMin: Number, ageMax: Number, interests: [String] },
placement: { type: String, enum: ['feed', 'story', 'reel', 'explore'] },
impressions: { type: Number, default: 0 },
clicks: { type: Number, default: 0 },
isActive: { type: Boolean, default: true },
startDate: Date,
endDate: Date
}
cd client
npm run build
vercel deploy --prod
Set environment variables in Vercel dashboard.
# On your server
git pull origin main
cd server
npm install --production
npm start
Or use the provided Dockerfile:
docker build -t instaclone-server ./server
docker run -p 5000:5000 --env-file server/.env instaclone-server
docker-compose up --build
This spins up MongoDB, Redis, the Node.js server, and the React client together.
NODE_ENV=production setsignedContributions are welcome. Please follow this workflow:
git checkout -b feature/your-feature-namegit commit -m "feat: add your feature"git push origin feature/your-feature-namefeat: New feature
fix: Bug fix
docs: Documentation update
style: Formatting, no logic change
refactor: Code restructure, no feature change
test: Adding tests
chore: Build process, dependency updates
This project is licensed under the MIT License β see the LICENSE file for details.
Built with β€οΈ using the MERN Stack. Not affiliated with Meta or Instagram.