Open In App

Social Media Platform using MERN Stack

Last Updated : 23 Jul, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

In web development, creating a "Social Media Website" will showcase and utilising the power of MERN stack – MongoDB, Express, React, and Node. This application will provide users the functionality to add a post, like the post and able to comment on it.

Preview Image: Let us have a look at how the final output will look like.
wf

Prerequisites:

Approach to create Social Media Platform:

  • The social media website was developed with a dual focus on backend using Express.js and frontend using React.
  • Express handled API routes for CRUD operations, including likes and comments, and Multer facilitated file uploads for multimedia content.
  • React was chosen for the frontend, providing an interactive user interface with components for posts, likes, and comments.
  • Axios played a pivotal role in connecting the frontend to the backend API endpoints, ensuring smooth communication.
  • The integration phase involved configuring CORS, connecting frontend to backend API URLs, and thorough end-to-end testing for a seamless user experience.

Steps to Create the Project:

Step 1: Create a directory for the backend by running the following command.

npm init social_backend
cd social_backend

Step 2: Initialize the Express project and install the following dependencies.

npm init -y
npm install express mongoose cors body-parser multer uuid

Folder Structure(Backend):

ba

The updated dependencies in package.json file of backend will look like:

"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^8.0.3",
"multer": "^1.4.5-lts.1",
"uuid": "^9.0.1"
}

Example: Create the required files and add the following code:

JavaScript
// models/Post.js

const mongoose = require('mongoose');

const postSchema = new mongoose.Schema({
    title: String,
    content: String,
    likes: { type: Number, default: 0 },
    comments: [{ text: String }],
});

const Post = mongoose.model('Post', postSchema);

module.exports = Post;
JavaScript
// server.js

const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');
const multer = require('multer');
const path = require('path');

const app = express();
const PORT = process.env.PORT || 5000;

app.use(cors());
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));

const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, 'uploads/');
    },
    filename: function (req, file, cb) {
        cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
    }
});

const upload = multer({ storage: storage });

mongoose.connect('Your MongoDB connection string', { useNewUrlParser: true, useUnifiedTopology: true });

const postSchema = new mongoose.Schema({
    title: String,
    content: String,
    file: String,
    likes: { type: Number, default: 0 },
    comments: [{ text: String }],
});

const Post = mongoose.model('Post', postSchema);

app.use(bodyParser.json());

app.get('/api/posts', async (req, res) => {
    try {
        const posts = await Post.find();
        res.json(posts);
    } catch (error) {
        res.status(500).json({ error: 'Internal Server Error' });
    }
});

app.post('/api/posts', upload.single('file'), async (req, res) => {
    try {
        const { title, content } = req.body;
        const file = req.file ? req.file.filename : undefined;

        if (!title || !content) {
            return res.status(400).json({ error: 'Title and content are required fields' });
        }

        const post = new Post({ title, content, file });
        await post.save();
        res.status(201).json(post);
    } catch (error) {
        console.error('Error creating post:', error);
        res.status(500).json({ error: 'Internal Server Error' });
    }
});

app.post('/api/posts/like/:postId', async (req, res) => {
    try {
        const postId = req.params.postId;
        const post = await Post.findById(postId);

        if (!post) {
            return res.status(404).json({ error: 'Post not found' });
        }

        post.likes += 1;
        await post.save();

        res.json(post);
    } catch (error) {
        console.error('Error liking post:', error);
        res.status(500).json({ error: 'Internal Server Error' });
    }
});

app.post('/api/posts/comment/:postId', async (req, res) => {
    try {
        const postId = req.params.postId;
        const { text } = req.body;
        const post = await Post.findById(postId);

        if (!post) {
            return res.status(404).json({ error: 'Post not found' });
        }

        post.comments.push({ text });
        await post.save();

        res.json(post);
    } catch (error) {
        console.error('Error adding comment:', error);
        res.status(500).json({ error: 'Internal Server Error' });
    }
});

app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

Step 3: To start the backend run the following command.

node server.js

Step 4: Set up React frontend using the command.

npx create-react-app social_frontend
cd social_frontend

Step 5 : Installing the required packages:

npm i axios react-router-dom

Folder Structure(Frontend):

fr

The updated dependencies in package.json file of frontend will look lik:

"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.21.1",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}

Example: Create the required files and the following code.

CSS
/* App.css */

.home {
    max-width: 800px;
    margin: 0 auto;
}

.post {
    border: 1px solid #ddd;
    padding: 15px;
    margin-bottom: 20px;
}

.post h3 {
    color: #333;
}

.post p {
    color: #555;
}

/* App.css */

.create-post {
    max-width: 600px;
    margin: 20px auto;
    padding: 20px;
    border: 1px solid #ddd;
    background-color: #fff;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

.create-post h2 {
    color: #333;
}

.create-post input,
.create-post textarea {
    width: 100%;
    margin: 10px 0;
    padding: 10px;
}

.create-post button {
    background-color: #4caf50;
    color: #fff;
    padding: 10px 15px;
    border: none;
    cursor: pointer;
}

.comment-input {
    margin-top: 10px;
    padding: 8px;
    width: 70%;
}

.comment-button {
    background-color: #4caf50;
    color: #fff;
    padding: 8px 16px;
    border: none;
    cursor: pointer;
}

.post img,
.post video {
    max-width: 100%;
    height: auto;
    margin-top: 10px;
}

.post button {
    background-color: #4caf50;
    color: #fff;
    padding: 8px 16px;
    border: none;
    cursor: pointer;
    margin-right: 10px;
}

.post ul {
    list-style: none;
    padding: 0;
}

.post li {
    margin-bottom: 5px;
}

.comment-input {
    margin-top: 10px;
    padding: 8px;
    width: 70%;
}

.comment-button {
    background-color: #4caf50;
    color: #fff;
    padding: 8px 16px;
    border: none;
    cursor: pointer;
}


/* App.css */

.app {
    max-width: 800px;
    margin: 0 auto;
}

nav {
    background-color: #333;
    padding: 10px;
}

nav ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

nav li {
    display: inline-block;
    margin-right: 20px;
}

nav a {
    text-decoration: none;
    color: #fff;
    font-weight: bold;
    font-size: 16px;
}

nav a:hover {
    color: #4caf50;
}

.create-post,
.home {
    border: 1px solid #ddd;
    padding: 20px;
    margin-bottom: 20px;
    background-color: #fff;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

.home h2,
.create-post h2 {
    color: #333;
}

.home .post,
.create-post {
    margin-bottom: 30px;
}

.home .post button,
.create-post button {
    background-color: #4caf50;
    color: #fff;
    padding: 10px 15px;
    border: none;
    cursor: pointer;
}

.home .post button:hover,
.create-post button:hover {
    background-color: #45a049;
}
JavaScript
// App.js

import React from 'react';
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import Home from './Home';
import CreatePost from './CreatePost';
import './App.css';

function App() {
	return (
		<Router>
			<div className="app">
				<nav>
					<ul>
						<li>
							<Link to="/">Home</Link>
						</li>
						<li>
							<Link to="/create">Create Post</Link>
						</li>
					</ul>
				</nav>
				<Routes>
					<Route path="/create" element={<CreatePost />} />
					<Route path="/" element={<Home />} />
				</Routes>
			</div>
		</Router>
	);
}

export default App;
JavaScript
// Home.js

import React, { useState, useEffect } from "react";
import axios from "axios";

function Home() {
	const [commentInput, setCommentInput] = useState("");
	const [posts, setPosts] = useState([]);

	useEffect(() => {
		axios
			.get("http://localhost:5000/api/posts")
			.then((response) => setPosts(response.data))
			.catch((error) => console.error("Error fetching posts:", error));
	}, []);

	const handleLike = (postId) => {
		axios
			.post(`http://localhost:5000/api/posts/like/${postId}`)
			.then((response) => {
				const updatedPosts = posts.map((post) =>
					post._id === postId ? response.data : post
				);
				setPosts(updatedPosts);
			})
			.catch((error) => console.error("Error liking post:", error));
	};

	const handleAddComment = (postId, commentText) => {
		axios
			.post(`http://localhost:5000/api/posts/comment/${postId}`, {
				text: commentText,
			})
			.then((response) => {
				const updatedPosts = posts.map((post) =>
					post._id === postId ? response.data : post
				);
				setPosts(updatedPosts);
			})
			.catch((error) => console.error("Error adding comment:", error));
	};

	return (
		<div className="home">
			<h2>Recent Posts</h2>
			{posts.map((post) => (
				<div key={post._id} className="post">
					<h3>{post.title}</h3>
					<p>{post.content}</p>
					{post.file && (
						<div>
							{post.file.includes(".mp4") ? (
								<video width="320" height="240" controls>
									<source
										src={
									`http://localhost:5000/uploads/${post.file}`
										}
										type="video/mp4"
									/>
									Your browser does not support the video tag.
								</video>
							) : (
								<img
									src={
									`http://localhost:5000/uploads/${post.file}`
									}
									alt="Post Media"
								/>
							)}
						</div>
					)}
					<p>Likes: {post.likes}</p>
					<button onClick={() => handleLike(post._id)}>Like</button>
					<p>Comments: {post.comments.length}</p>
					<ul>
						{post.comments.map((comment, index) => (
							<li key={index}>{comment.text}</li>
						))}
					</ul>

					<input
						type="text"
						placeholder="Add a comment"
						className="comment-input"
						onChange={(e) => setCommentInput(e.target.value)}
					/>
					<button
						onClick={() => handleAddComment(post._id, commentInput)}
						className="comment-button"
					>
						Add Comment
					</button>
				</div>
			))}
		</div>
	);
}

export default Home;
JavaScript
// CreatePost.js
import React, { useState } from "react";
import axios from "axios";

function CreatePost() {
	const [newPost, setNewPost] = useState({
		title: "",
		content: "",
		file: null,
	});

	const handleInputChange = (event) => {
		const { name, value } = event.target;
		setNewPost({ ...newPost, [name]: value });
	};

	const handleFileChange = (event) => {
		setNewPost({ ...newPost, file: event.target.files[0] });
	};

	const handlePostSubmit = () => {
		const formData = new FormData();
		formData.append("title", newPost.title);
		formData.append("content", newPost.content);
		formData.append("file", newPost.file);

		axios
			.post("http://localhost:5000/api/posts", formData)
			.then((response) => {
				setNewPost({ title: "", content: "", file: null });
			})
			.catch((error) => console.error("Error creating post:", error));
	};

	return (
		<div className="create-post">
			<h2>Create a Post</h2>
			<input
				type="text"
				name="title"
				placeholder="Title"
				value={newPost.title}
				onChange={handleInputChange}
			/>
			<textarea
				name="content"
				placeholder="Content"
				value={newPost.content}
				onChange={handleInputChange}
			></textarea>
			<input type="file" name="file" onChange={handleFileChange} />
			<button onClick={handlePostSubmit}>Post</button>
		</div>
	);
}

export default CreatePost;

Step 6: Start the application by running the following command.

npm start

Output:


Similar Reads