Social media platforms are integral to modern communication and have a range of key features. Here's an overview of some of the primary features you might find across different social media platforms:
Key Features
User Profiles:
- Personal Information: Users can create profiles that include personal details, profile pictures, and bios.
- Customizable Privacy Settings: Allows users to control who can see their information and posts.
- Feeds and Timelines: Displays all the posts, user comments and likes.
- Home Feed: Displays a chronological or algorithmically sorted list of posts from users or pages followed.
- News Feed: A dynamic stream of updates, news, and content relevant to the user's interests.
Post Creation:
- Text Posts: Share thoughts, updates, and status messages.
- Multimedia Posts: Include images, videos, and audio clips.
- Stories: Short-lived posts that disappear after a set time (e.g., 24 hours).
Engagement Features:
- Likes and Reactions: Users can express approval or emotion towards posts.
- Comments: Engage in discussions or provide feedback on posts.
- Shares/Retweets: Repost content to your own feed or share with your followers.
Messaging and Communication:
- Direct Messaging: Private one-on-one or group conversations.
- Chatbots: Automated responses or interactions through messaging.
Notifications:
- Activity Alerts: Notify users about interactions, mentions, or updates from followed accounts.
- Customizable Settings: Users can manage their notification preferences.
Approach to Implement Social Media Platforms: feed
Backend
- Setup: Initialize an Express application with middleware for JSON parsing, CORS, and cookie parsing.
- Socket Integration: Configure Socket.IO for real-time communication and integrate with a SocketServer.
- Routes: Define API routes for authentication, user management, posts, comments, notifications, and messaging.
- Database Connection: Connect to MongoDB using Mongoose with provided credentials.
- Post Model: Create a Mongoose schema and model for handling posts, including fields for content, images, likes, comments, and reports.
- Post Controller: Implement CRUD operations for posts, including features like liking, unliking, saving, and reporting posts.
- Pagination: Use a custom APIfeatures class to handle pagination and sorting of posts.
- Error Handling: Include error handling for database operations and invalid requests to ensure robust API responses.
Frontend
- Post Component: Displays individual posts with user info, images, content, likes, saves, comments, and interactions using Material-UI components.
- PostPage Component: Fetches and displays a single post by ID, handling loading and error states.
- HomePage Component: Manages a list of posts, including fetching posts, handling likes, saves, and unsaves with user feedback via Snackbar.
- State Management: Uses useState and useEffect hooks for managing component state and side effects.
- API Integration: Utilizes axios for API requests, including handling authentication via tokens and updating the UI based on API responses.
Backend Example:
// index.js
require("dotenv").config()
const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
const cookieParser = require("cookie-parser");
const SocketServer = require("./socketServer");
const app = express();
app.use(express.json())
app.use(cors({
origin: [, "http://localhost:5173"],
methods: [
"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"
],
allowedHeaders: ["Content-Type", "Authorization"],
credentials: true,
}));
app.use(cookieParser())
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
next();
})
//#region // !Socket
const http = require("http").createServer(app);
const io = require("socket.io")(http);
io.on("connection", socket => { SocketServer(socket); })
//#endregion
app.get("/",
(req, res) => {
res.send(
"Hi Welcome to Social Media App API.....")
})
//#region // !Routes
app.use("/api", require("./routes/authRouter"));
app.use("/api", require("./routes/userRouter"));
app.use("/api", require("./routes/postRouter"));
//#endregion
const URI = "Your MongoDB URI";
mongoose.connect(URI, {
useCreateIndex: true,
useFindAndModify: false,
useNewUrlParser: true,
useUnifiedTopology: true
},
err => {
if (err)
throw err;
console.log("Database Connected!!")
})
const port = process.env.PORT || 3001;
http.listen(port,
() => { console.log("Listening on ", port); });
() => { console.log("Listening on ", port); });
// models/postModel.js
const mongoose = require("mongoose");
const { Schema } = mongoose;
const postSchema = new Schema(
{
content: String,
images: {
type: Array,
required: true,
},
likes: [
{
type: mongoose.Types.ObjectId,
ref: "user",
},
],
comments: [
{
type: mongoose.Types.ObjectId,
ref: "comment",
},
],
user: {
type: mongoose.Types.ObjectId,
ref: "user",
},
reports: [
{
type: mongoose.Types.ObjectId,
ref: "user",
},
],
},
{
timestamps: true,
}
);
module.exports = mongoose.model('post', postSchema);
// middleware/auth.js
const Users = require("../models/userModel");
const jwt = require("jsonwebtoken");
const auth = async (req, res, next) => {
try {
const authHeader = req.header("Authorization");
// Check if the token exists
if (!authHeader) {
return res.status(400).json({ msg: "You are not authorized" });
}
// Extract the token by removing 'Bearer ' prefix
const token = authHeader.split(" ")[1];
// Verify the token
const decoded = jwt.verify(token, "AAAA");
if (!decoded) {
return res.status(400).json({ msg: "Invalid token" });
}
const user = await Users.findOne({ _id: decoded.id });
req.user = user;
next();
} catch (err) {
return res.status(500).json({ msg: err.message });
}
}
module.exports = auth;
// middleware/upload.js
const multer = require("multer");
const path = require("path");
// Define Storage for Multer
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "uploads/"); // Upload destination folder
},
filename: (req, file, cb) => {
cb(null,
Date.now()
+ path.extname(
file.originalname)); // Append timestamp
// to file name
},
});
// File filter to only allow image types
const fileFilter = (req, file, cb) => {
const filetypes = /jpeg|jpg|png|gif/;
const mimetype = filetypes.test(file.mimetype);
const extname = filetypes.test(
path.extname(file.originalname).toLowerCase());
if (mimetype && extname) {
return cb(null, true);
}
cb(new Error(
"Only images (jpeg, jpg, png, gif) are allowed"));
};
// Set up Multer with storage, file size limit, and file
// filter
const upload = multer({
storage: storage,
limits: { fileSize: 5 * 1024 * 1024 }, // 5 MB limit
fileFilter: fileFilter,
});
// Exporting the Multer upload function
module.exports = upload;
// contollers/postCtrl
const Posts = require("../models/postModel");
const Comments = require("../models/commentModel");
const Users = require("../models/userModel");
class APIfeatures {
constructor(query, queryString) {
this.query = query;
this.queryString = queryString;
}
paginating() {
const page = this.queryString.page * 1 || 1;
const limit = this.queryString.limit * 1 || 9;
const skip = (page - 1) * limit;
this.query = this.query.skip(skip).limit(limit);
return this;
}
}
const postCtrl = {
createPost: async (req, res) => {
try {
const { content } = req.body;
// If no files (images) were uploaded
if (!req.files || req.files.length === 0) {
return res.status(400).json(
{ msg: "Please add photo(s)" });
}
// Extract image paths from uploaded files
const imagePaths
= req.files.map(file => file.path);
// Create a new post with the content and image
// paths
const newPost = new Posts({
content,
images: imagePaths,
user: req.user._id, // Assuming req.user is
// populated from auth
// middleware
});
// Save the new post to the database
await newPost.save();
// Return a response with the created post data
res.json({
msg: "Post created successfully.",
newPost: {
...newPost._doc,
user: req.user, // Include user data in
// the response
},
});
}
catch (err) {
return res.status(500).json(
{ msg: err.message });
}
},
getPosts: async (req, res) => {
try {
const features = new APIfeatures(Posts.find({
user: [
...req.user.following,
req.user._id
],
}),
req.query)
.paginating();
const posts
= await features.query.sort("-createdAt")
.populate(
"user likes",
"avatar username fullname followers")
.populate({
path: "comments",
populate: {
path: "user likes ",
select: "-password",
},
});
res.json({
msg: "Success",
result: posts.length,
posts,
});
}
catch (err) {
return res.status(500).json(
{ msg: err.message });
}
},
updatePost: async (req, res) => {
try {
const { content, images } = req.body;
const post
= await Posts
.findOneAndUpdate(
{ _id: req.params.id }, {
content,
images,
})
.populate("user likes",
"avatar username fullname")
.populate({
path: "comments",
populate: {
path: "user likes ",
select: "-password",
},
});
res.json({
msg: "Post updated successfully.",
newPost: {
...post._doc,
content,
images,
},
});
}
catch (err) {
return res.status(500).json(
{ msg: err.message });
}
},
likePost: async (req, res) => {
try {
const post = await Posts.find({
_id: req.params.id,
likes: req.user._id,
});
if (post.length > 0) {
return res.status(400).json({
msg: "You have already liked this post"
});
}
const like = await Posts.findOneAndUpdate(
{ _id: req.params.id }, {
$push: { likes: req.user._id },
},
{
new: true,
});
if (!like) {
return res.status(400).json(
{ msg: "Post does not exist." });
}
res.json({ msg: "Post liked successfully." });
}
catch (err) {
return res.status(500).json(
{ msg: err.message });
}
},
unLikePost: async (req, res) => {
try {
const like = await Posts.findOneAndUpdate(
{ _id: req.params.id }, {
$pull: { likes: req.user._id },
},
{
new: true,
});
if (!like) {
return res.status(400).json(
{ msg: "Post does not exist." });
}
res.json({ msg: "Post unliked successfully." });
}
catch (err) {
return res.status(500).json(
{ msg: err.message });
}
},
getUserPosts: async (req, res) => {
try {
const features
= new APIfeatures(
Posts.find({ user: req.params.id }),
req.query)
.paginating();
const posts
= await features.query.sort("-createdAt");
res.json({
posts,
result: posts.length,
});
}
catch (err) {
return res.status(500).json(
{ msg: err.message });
}
},
getPost: async (req, res) => {
try {
const post
= await Posts.findById(req.params.id)
.populate(
"user likes",
"avatar username fullname followers")
.populate({
path: "comments",
populate: {
path: "user likes ",
select: "-password",
},
});
if (!post) {
return res.status(400).json(
{ msg: "Post does not exist." });
}
res.json({ post });
}
catch (err) {
return res.status(500).json(
{ msg: err.message });
}
},
getPostDiscover: async (req, res) => {
try {
const newArr =
[...req.user.following, req.user._id];
const num = req.query.num || 8;
const posts = await Posts.aggregate([
{ $match: { user: { $nin: newArr } } },
{ $sample: { size: Number(num) } },
]);
res.json({
msg: "Success",
result: posts.length,
posts,
});
}
catch (err) {
return res.status(500).json(
{ msg: err.message });
}
},
deletePost: async (req, res) => {
try {
const post = await Posts.findOneAndDelete({
_id: req.params.id,
user: req.user._id,
});
await Comments.deleteMany(
{ _id: { $in: post.comments } });
res.json({
msg: "Post deleted successfully.",
newPost: { ...post, user: req.user }
});
}
catch (err) {
return res.status(500).json(
{ msg: err.message });
}
},
reportPost: async (req, res) => {
try {
const post = await Posts.find({
_id: req.params.id,
reports: req.user._id,
});
if (post.length > 0) {
return res.status(400).json({
msg:
"You have already reported this post"
});
}
const report = await Posts.findOneAndUpdate(
{ _id: req.params.id }, {
$push: { reports: req.user._id },
},
{
new: true,
});
if (!report) {
return res.status(400).json(
{ msg: "Post does not exist." });
}
res.json({ msg: "Post reported successfully." });
}
catch (err) {
return res.status(500).json(
{ msg: err.message });
}
},
savePost: async (req, res) => {
try {
const user = await Users.find({
_id: req.user._id,
saved: req.params.id,
});
if (user.length > 0) {
return res.status(400).json({
msg:
"You have already saved this post."
});
}
const save = await Users.findOneAndUpdate(
{ _id: req.user._id }, {
$push: { saved: req.params.id },
},
{
new: true,
});
if (!save) {
return res.status(400).json(
{ msg: "User does not exist." });
}
res.json({ msg: "Post saved successfully." });
}
catch (err) {
return res.status(500).json(
{ msg: err.message });
}
},
unSavePost: async (req, res) => {
try {
const save = await Users.findOneAndUpdate(
{ _id: req.user._id }, {
$pull: { saved: req.params.id },
},
{
new: true,
});
if (!save) {
return res.status(400).json(
{ msg: "User does not exist." });
}
res.json({
msg:
"Post removed from collection successfully."
});
}
catch (err) {
return res.status(500).json(
{ msg: err.message });
}
},
getSavePost: async (req, res) => {
try {
const features
= new APIfeatures(
Posts.find(
{ _id: { $in: req.user.saved } }),
req.query)
.paginating();
const savePosts
= await features.query.sort("-createdAt");
res.json({ savePosts, result: savePosts.length })
}
catch (err) {
return res.status(500).json(
{ msg: err.message });
}
},
};
module.exports = postCtrl;
Start Your backend using the below command:
node index.jsFrontend Example:
// App.jsx
import React from "react";
import {
BrowserRouter as Router,
Route,
Routes
} from "react-router-dom";
import Login from "./components/Login";
import Navbar from "./components/Navbar";
import Register from "./components/Register";
import UserProfile from "./components/UserProfile.jsx";
import CreatePost from "./pages/CreatePost.jsx";
import HomePage from "./pages/HomePage";
import PostPage from "./pages/PostPage";
import SavedPostsPage from "./pages/SavedPostsPage";
import UserPostsPage from "./pages/UserPostsPage";
const App = () => {
return (
<Router><Navbar /><Routes>< Route path="/" element
={<HomePage />} />
<Route path="/posts
/: id " element={<PostPage />} />
< Route path
="/user-posts" element
={<UserPostsPage />} />
<Route path="/post
" element={<CreatePost />} />
< Route path
="/saved-posts" element
={<SavedPostsPage />} />
<Route path="/user
/: id " element={<UserProfile />} />
< Route path
="/register" element
={<Register />} />
<Route path="/login
" element={<Login />} />
< /Routes>
</Router >);
};
export default App;
// pages/SavedPostPages.jsx
import axios from "axios";
import React, { useEffect, useState } from "react";
import Post from "../components/Post";
const SavedPostsPage = () => {
const [savedPosts, setSavedPosts] = useState([]);
useEffect(() => {
axios.get(`http://localhost:3001/api/getSavePosts%60, {
headers: {
Authorization: `Bearer ${token}`,
},
})
.then(response => {
setSavedPosts(response.data.savePosts);
})
.catch(error => {
console.log(error);
});
}, []);
return (
<div>
{savedPosts.map(post => (
<Post key={post._id} post={post} />
))}
</div>
);
};
export default SavedPostsPage;
// src/pages/PostPage.jsx
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import axios from 'axios';
import Post from '../components/Post';
const PostPage = () => {
const { id } = useParams();
const [post, setPost] = useState(null);
useEffect(() => {
axios.get(`http://localhost:3001/api/posts/$%7Bid%7D%60)
.then(response => {
setPost(response.data.post);
})
.catch(error => {
console.log(error);
});
}, [id]);
return post ? <Post post={post} /> : <div>Loading...</div>;
};
export default PostPage;
// src/pages/HomePage.jsx
import {
Alert,
CircularProgress,
Container,
Snackbar
} from "@mui/material";
import axios from "axios";
import React, { useEffect, useState } from "react";
import Post from "../components/Post";
const HomePage = () => {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true); // Loading state
const [error, setError] = useState(null); // Error state
const [openSnackbar, setOpenSnackbar] = useState(false); // Snackbar for feedback
const [snackbarMessage, setSnackbarMessage] = useState("");
const token = localStorage.getItem("token");
useEffect(() => {
axios.get("http://localhost:3001/api/posts", {
headers: {
Authorization: `Bearer ${token}`,
},
})
.then(response => {
setPosts(response.data.posts);
setLoading(false);
})
.catch(error => {
setError("Failed to load posts. Please try again later.");
setLoading(false);
});
}, [token]);
const handleSnackbar = (message) => {
setSnackbarMessage(message);
setOpenSnackbar(true);
};
const handleLike = (postId) => {
axios.patch(`http://localhost:3001/api/post/$%7BpostId%7D/like%60, {}, {
headers: { Authorization: `Bearer ${token}` }
}).then(() => {
handleSnackbar("Post liked!");
setPosts(prevPosts =>
prevPosts.map(post =>
post._id === postId ? { ...post, isLiked: true, likes: post.likes + 1 } : post
)
);
});
};
const handleUnlike = (postId) => {
axios.patch(`http://localhost:3001/api/post/$%7BpostId%7D/unlike%60, {}, {
headers: { Authorization: `Bearer ${token}` }
}).then(() => {
handleSnackbar("Post unliked!");
setPosts(prevPosts =>
prevPosts.map(post =>
post._id === postId ? { ...post, isLiked: false, likes: post.likes - 1 } : post
)
);
});
};
const handleSave = (postId) => {
axios.patch(`http://localhost:3001/api/post/$%7BpostId%7D/save%60, {}, {
headers: { Authorization: `Bearer ${token}` }
}).then(() => {
handleSnackbar("Post saved!");
setPosts(prevPosts =>
prevPosts.map(post =>
post._id === postId ? { ...post, isSaved: true } : post
)
);
});
};
const handleUnsave = (postId) => {
axios.patch(`http://localhost:3001/api/post/$%7BpostId%7D/unsave%60, {}, {
headers: { Authorization: `Bearer ${token}` }
}).then(() => {
handleSnackbar("Post unsaved!");
setPosts(prevPosts =>
prevPosts.map(post =>
post._id === postId ? { ...post, isSaved: false } : post
)
);
});
};
return (
<Container maxWidth="md">
{/* Loading spinner */}
{loading && <CircularProgress style={{ display: "block", margin: "20px auto" }} />}
{/* Error message */}
{error && <Alert severity="error" style={{ marginBottom: '20px' }}>{error}</Alert>}
{/* Posts */}
{!loading && !error && posts.length > 0 && (
posts.map(post => (
<Post
key={post._id}
post={post}
handleLike={() => handleLike(post._id)}
handleUnlike={() => handleUnlike(post._id)}
handleSave={() => handleSave(post._id)}
handleUnsave={() => handleUnsave(post._id)}
isLiked={post.isLiked}
isSaved={post.isSaved}
/>
))
)}
{/* Snackbar for user feedback */}
<Snackbar
open={openSnackbar}
autoHideDuration={3000}
onClose={() => setOpenSnackbar(false)}
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
>
<Alert onClose={() => setOpenSnackbar(false)} severity="success">
{snackbarMessage}
</Alert>
</Snackbar>
</Container>
);
};
export default HomePage;
// src/components/Post.jsx
import React from 'react';
import {
Card,
CardHeader,
CardMedia,
CardContent,
Typography,
IconButton,
Avatar,
Grid,
Tooltip,
Divider
} from '@mui/material';
import {
Favorite,
FavoriteBorder,
Bookmark,
BookmarkBorder,
ChatBubbleOutline,
Share
} from '@mui/icons-material';
const Post = ({ post, handleLike, handleUnlike, handleSave, handleUnsave, isLiked, isSaved }) => {
const { user, content, images, likes, comments, createdAt } = post;
return (
<Card style={{ margin: '20px 0' }}>
{/* User Info */}
<CardHeader
avatar={<Avatar src={user?.avatar} alt={user?.username} />}
title={<Typography variant="h6">{user?.username}</Typography>}
subheader={new Date(createdAt).toLocaleDateString()}
/>
{/* Post Image */}
{images && images[0] && (
<CardMedia
component="img"
height="300"
image={images[0]} // Use images[0] safely
alt="Post Image"
style={{ objectFit: 'cover' }}
/>
)}
{/* Post Content */}
<CardContent>
<Typography variant="body1" style={{ marginBottom: '10px' }}>
{content}
</Typography>
<Grid container alignItems="center" spacing={2}>
{/* Like and Unlike */}
<Grid item>
<IconButton onClick={isLiked ? handleUnlike : handleLike}>
{isLiked ? (
<Favorite color="error" />
) : (
<FavoriteBorder />
)}
</IconButton>
<Typography variant="body2" component="span">{likes.length} Likes</Typography>
</Grid>
{/* Save and Unsave */}
<Grid item>
<IconButton onClick={isSaved ? handleUnsave : handleSave}>
{isSaved ? (
<Bookmark />
) : (
<BookmarkBorder />
)}
</IconButton>
</Grid>
{/* Comment Section */}
<Grid item>
<Tooltip title="Comment">
<IconButton>
<ChatBubbleOutline />
</IconButton>
</Tooltip>
</Grid>
{/* Share */}
<Grid item>
<Tooltip title="Share">
<IconButton>
<Share />
</IconButton>
</Tooltip>
</Grid>
</Grid>
<Divider style={{ margin: '10px 0' }} />
{/* Comment Section */}
<Typography variant="subtitle1">Comments</Typography>
<Grid container spacing={2}>
{comments?.length ? comments.map((comment, index) => (
<Grid item xs={12} key={index}>
{comment.user && comment.user.username ? (
<Typography variant="body2">
<strong>{comment.user.username}:</strong> {comment.text}
</Typography>
) : (
<Typography variant="body2">Anonymous:</Typography>
)}
</Grid>
)) : (
<Typography variant="body2">No comments yet.</Typography>
)}
</Grid>
</CardContent>
</Card>
);
};
export default Post;
// src/components/Navbar.jsx
import {
AccountCircle,
Notifications,
Search
} from "@mui/icons-material";
import {
AppBar,
Avatar,
Badge,
Button,
IconButton,
InputBase,
Menu,
MenuItem,
Toolbar,
Typography
} from "@mui/material";
import { alpha, styled } from "@mui/material/styles";
import React, { useEffect, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
const SearchBar = styled("div")(
({ theme }) => ({
position: "relative",
borderRadius: theme.shape.borderRadius,
backgroundColor:
alpha(theme.palette.common.white, 0.15),
"&:hover": {
backgroundColor:
alpha(theme.palette.common.white, 0.25),
},
marginRight: theme.spacing(2),
marginLeft: 0,
width: "100%",
[theme.breakpoints.up("sm")]: {
marginLeft: theme.spacing(3),
width: "auto",
},
}));
const SearchIconWrapper
= styled("div")(({ theme }) => ({
padding: theme.spacing(0, 2),
height: "100%",
position: "absolute",
pointerEvents: "none",
display: "flex",
alignItems: "center",
justifyContent: "center",
}));
const StyledInputBase = styled(InputBase)(
({ theme }) => ({
color: "inherit",
"& .MuiInputBase-input": {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)})`,
transition: theme.transitions.create("width"),
width: "100%",
[theme.breakpoints.up("md")]: {
width: "20ch",
},
},
}));
const Navbar = () => {
const [anchorEl, setAnchorEl] = useState(null);
const [user, setUser] = useState(null);
const navigate = useNavigate();
useEffect(() => {
const loggedUser = localStorage.getItem("user");
if (loggedUser) {
setUser(JSON.parse(loggedUser));
}
}, []);
const handleProfileMenuOpen = (event) => {
setAnchorEl(event.currentTarget);
};
const handleMenuClose = () => {
setAnchorEl(null);
};
const handleLogout = () => {
localStorage.removeItem("user"); // Remove user from localStorage
localStorage.removeItem("token"); // Remove token from localStorage
setUser(null); // Set user state to null
navigate("/login"); // Redirect to login page
};
const isMenuOpen = Boolean(anchorEl);
const menuId = "primary-search-account-menu";
return (
<AppBar position="static">
<Toolbar>
<Typography variant="h6" sx={{ flexGrow: 1 }}>
Social Media App
</Typography>
{/* Search Bar */}
<SearchBar>
<SearchIconWrapper>
<Search />
</SearchIconWrapper>
<StyledInputBase
placeholder="SearchâĶ"
inputProps={{ 'aria-label': 'search' }}
/>
</SearchBar>
{/* Navigation Links */}
<Button color="inherit" component={Link} to="/">Feed</Button>
<Button color="inherit" component={Link} to="/post">Create Post</Button>
<Button color="inherit" component={Link} to="/user-posts">My Posts</Button>
<Button color="inherit" component={Link} to="/saved-posts">Saved Posts</Button>
{/* Notifications */}
<IconButton color="inherit">
<Badge badgeContent={4} color="error">
<Notifications />
</Badge>
</IconButton>
{user?.fullname ? (
<>
{/* User Profile Dropdown */}
<IconButton
edge="end"
aria-label="account of current user"
aria-controls={menuId}
aria-haspopup="true"
onClick={handleProfileMenuOpen}
color="inherit"
>
<AccountCircle />
</IconButton>
{/* Dropdown Menu */}
<Menu
anchorEl={anchorEl}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
id={menuId}
keepMounted
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
open={isMenuOpen}
onClose={handleMenuClose}
>
<MenuItem component={Link} to={`/user/${user._id}`}>Profile</MenuItem>
<MenuItem component={Link} to="/settings">Settings</MenuItem>
<MenuItem onClick={handleLogout}>Logout</MenuItem>
</Menu>
</>
) : (
<Button color="inherit" component={Link} to="/login">Login</Button>
)}
</Toolbar>
</AppBar>
);
};
export default Navbar;
Start your frontend using the below command:
npm run devOutput