Open In App

Creating User Profiles in MERN Stack Social Media Platform

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

In today's interconnected world, social media platforms have become integral to daily communication, self-expression, and networking. At the heart of these platforms are user profiles, which act as digital representations of individuals, allowing them to craft an online persona that can be shared, followed, and engaged with by others. But what exactly constitutes a user profile, and how does it impact the social media experience?

What is a User Profile?

A user profile is essentially a curated digital identity that contains personal information, interests, and activities. Depending on the platform, profiles can range from basic (such as name, profile picture, and bio) to highly detailed (including preferences, likes, and activity history). Common elements across profiles include:

  • Name and Username: While the name often reflects the real identity of the user, the username acts as a unique handle.
  • Profile Picture: This visual element is often the first point of interaction with others.
  • Bio/Description: A short self-introduction where users express themselves in a few words.
  • Activity and Posts: Most profiles include posts, tweets, photos, or videos that give others insights into the user’s activities or opinions.

The Role of Personalization

User profiles provide a sense of personalization, which enhances the user experience. By allowing individuals to showcase their interests, experiences, and personalities, profiles turn a static platform into a dynamic, interactive space. Personalization features include:

  • Customizable themes or backgrounds (on platforms like MySpace in the past).
  • Activity streams or timelines that highlight a user’s posts or interactions.
  • Highlighting achievements such as follower counts, badges, or special mentions.

Approach to Implement User Profiles

Backend:

  • Profile Schema: Defines user information like full name, username, email, avatar, and bio.
  • Authentication: Users authenticate via JWT tokens for secure access to their profiles.
  • Profile Updates: Users can update profile details such as avatar, bio, website, and other personal information.
  • Followers/Following: Profiles track user relationships via followers and following arrays.
  • Privacy Settings: Users control the visibility of their profiles (public/private).
  • Activity Feed: Profile activity, including posts and interactions, is displayed.
  • Profile Picture: A default avatar is provided, but users can upload custom profile pictures.
  • Profile Stats: Displays key metrics like number of posts, followers, and following.

Frontend:

  • Authentication Flow: The Login component handles user login, stores the token and user details in localStorage, and navigates to the user's profile upon successful login.
  • Profile Display: The UserProfile component fetches and displays the user’s profile details, including name, followers, and saved posts, and allows follow/unfollow functionality.
  • Registration: The Register component allows users to register by submitting their details via a form, including optional gender selection, and redirects them to the login page on success.
  • API Integration: Both the Login and Register components use axios for making HTTP requests to the backend for authentication and registration.
  • UI and Styling: Components utilize Material-UI (@mui/material) for consistent styling, with forms, buttons, icons, and layout elements like Grid and Box.

Steps to Setup Server

Step 1: Setting up backend

Create a directory for project

mkdir server
cd server

Step 2: Initialized the Express app and installing the required packages

npm init -y
npm i express mongoose cors dotenv body-parser

Project Structure

00
Project Structrue

Dependencies

"dependencies":{
"dotenv": "^16.3.1",
"express": "^4.18.2",
"cors": "^2.8.5",
"mongoose": "^8.0.3",
"body-parser": "^1.20.2",
}

Backend Example:

JavaScript
// 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', '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'));

//#endregion


const URI = process.env.MONGO_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);
});
JavaScript
// models/userModel.js

const mongoose = require('mongoose');
const { Schema } = mongoose;

const userSchema = new Schema(
    {
        fullname: {
            type: String,
            required: true,
            trim: true,
            maxlength: 25,
        },
        username: {
            type: String,
            required: true,
            trim: true,
            maxlength: 25,
            unique: true,
        },
        email: {
            type: String,
            required: true,
            trim: true,
            unique: true,
        },
        password: {
            type: String,
            required: true,
        },
        avatar: {
            type: String,
            default:
                "https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460__340.png",
        },
        role: {
            type: String,
            default: "user",
        },
        gender: {
            type: String,
            default: "male",
        },
        mobile: {
            type: String,
            default: "",
        },
        address: {
            type: String,
            default: "",
        },
        saved: [
            {
                type: mongoose.Types.ObjectId,
                ref: 'post'
            }
        ],
        story: {
            type: String,
            default: "",
            maxlength: 200,
        },
        website: {
            type: String,
            default: "",
        },
        followers: [
            {
                type: mongoose.Types.ObjectId,
                ref: "user",
            },
        ],
        following: [
            {
                type: mongoose.Types.ObjectId,
                ref: "user",
            },
        ],
    },
    {
        timestamps: true,
    }
);


module.exports = mongoose.model('user', userSchema);
JavaScript
// controllers/authCtrl.js

const Users = require("../models/userModel");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");

const authCtrl = {
    register: async (req, res) => {
        try {
            const { fullname, username, email, password, gender } = req.body;

            let newUserName = username.toLowerCase().replace(/ /g, "");

            const user_name = await Users.findOne({ username: newUserName });
            if (user_name) {
                return res.status(400).json({ msg: "This username is already taken." });
            }

            const user_email = await Users.findOne({ email });
            if (user_email) {
                return res
                    .status(400)
                    .json({ msg: "This email is already registered." });
            }

            if (password.length < 6) {
                return res
                    .status(400)
                    .json({ msg: "Password must be at least 6 characters long." });
            }

            const passwordHash = await bcrypt.hash(password, 12);

            const newUser = new Users({
                fullname,
                username: newUserName,
                email,
                password: passwordHash,
                gender,
            });

            const access_token = createAccessToken({ id: newUser._id });
            const refresh_token = createRefreshToken({ id: newUser._id });

            res.cookie("refreshtoken", refresh_token, {
                httpOnly: true,
                path: "/api/refresh_token",
                maxAge: 30 * 24 * 60 * 60 * 1000, //validity of 30 days
            });

            res.json({
                msg: "Registered Successfully!",
                access_token,
                user: {
                    ...newUser._doc,
                    password: "",
                },
            });

            await newUser.save();

            res.json({ msg: "registered" });
        } catch (err) {
            return res.status(500).json({ msg: err.message });
        }
    },

    changePassword: async (req, res) => {
        try {
            const { oldPassword, newPassword } = req.body;

            const user = await Users.findOne({ _id: req.user._id });

            const isMatch = await bcrypt.compare(oldPassword, user.password);
            if (!isMatch) {
                return res.status(400).json({ msg: "Your password is wrong." });
            }

            if (newPassword.length < 6) {
                return res
                    .status(400)
                    .json({ msg: "Password must be at least 6 characters long." });
            }

            const newPasswordHash = await bcrypt.hash(newPassword, 12);

            await Users.findOneAndUpdate({ _id: req.user._id }, { password: newPasswordHash });

            res.json({ msg: "Password updated successfully." })

        } catch (err) {
            return res.status(500).json({ msg: err.message });
        }
    },

    registerAdmin: async (req, res) => {
        try {
            const { fullname, username, email, password, gender, role } = req.body;

            let newUserName = username.toLowerCase().replace(/ /g, "");

            const user_name = await Users.findOne({ username: newUserName });
            if (user_name) {
                return res.status(400).json({ msg: "This username is already taken." });
            }

            const user_email = await Users.findOne({ email });
            if (user_email) {
                return res
                    .status(400)
                    .json({ msg: "This email is already registered." });
            }

            if (password.length < 6) {
                return res
                    .status(400)
                    .json({ msg: "Password must be at least 6 characters long." });
            }

            const passwordHash = await bcrypt.hash(password, 12);

            const newUser = new Users({
                fullname,
                username: newUserName,
                email,
                password: passwordHash,
                gender,
                role
            });




            await newUser.save();

            res.json({ msg: "Admin Registered Successfully." });
        } catch (err) {
            return res.status(500).json({ msg: err.message });
        }
    },

    login: async (req, res) => {
        try {
            const { email, password } = req.body;

            const user = await Users.findOne({ email, role: "user" }).populate(
                "followers following",
                "-password"
            );

            if (!user) {
                return res.status(400).json({ msg: "Email or Password is incorrect." });
            }

            const isMatch = await bcrypt.compare(password, user.password);
            if (!isMatch) {
                return res.status(400).json({ msg: "Email or Password is incorrect." });
            }

            const access_token = createAccessToken({ id: user._id });
            const refresh_token = createRefreshToken({ id: user._id });

            res.cookie("refreshtoken", refresh_token, {
                httpOnly: true,
                path: "/api/refresh_token",
                sameSite: 'lax',
                maxAge: 30 * 24 * 60 * 60 * 1000, //validity of 30 days
            });

            res.json({
                msg: "Logged in  Successfully!",
                access_token,
                user: {
                    ...user._doc,
                    password: "",
                },
            });
        } catch (err) {
            return res.status(500).json({ msg: err.message });
        }
    },

    adminLogin: async (req, res) => {
        try {
            const { email, password } = req.body;
            console.log("fd", password);
            const user = await Users.findOne({ email, role: "admin" });
            console.log("user", user)
            if (!user) {
                return res.status(400).json({ msg: "Email or Password is incorrect." });
            }

            const isMatch = await bcrypt.compare(password, user.password);
            if (!isMatch) {
                return res.status(400).json({ msg: "Email or Password is incorrect." });
            }

            const access_token = createAccessToken({ id: user._id });
            const refresh_token = createRefreshToken({ id: user._id });

            res.cookie("refreshtoken", refresh_token, {
                httpOnly: true,
                path: "/api/refresh_token",
                maxAge: 30 * 24 * 60 * 60 * 1000, //validity of 30 days
            });

            res.json({
                msg: "Logged in  Successfully!",
                access_token,
                user: {
                    ...user._doc,
                    password: "",
                },
            });
        } catch (err) {
            return res.status(500).json({ msg: err.message });
        }
    },

    logout: async (req, res) => {
        try {
            res.clearCookie("refreshtoken", { path: "/api/refresh_token" });
            return res.json({ msg: "Logged out Successfully." });
        } catch (err) {
            return res.status(500).json({ msg: err.message });
        }
    },

    generateAccessToken: async (req, res) => {
        try {
            const rf_token = req.cookies.refreshtoken;

            if (!rf_token) {
                return res.status(400).json({ msg: "Please login again." });
            }
            jwt.verify(
                rf_token,
                process.env.REFRESH_TOKEN_SECRET,
                async (err, result) => {
                    if (err) {
                        return res.status(400).json({ msg: "Please login again." });
                    }

                    const user = await Users.findById(result.id)
                        .select("-password")
                        .populate("followers following", "-password");

                    if (!user) {
                        return res.status(400).json({ msg: "User does not exist." });
                    }

                    const access_token = createAccessToken({ id: result.id });
                    res.json({ access_token, user });
                }
            );
        } catch (err) {
            return res.status(500).json({ msg: err.message });
        }
    },
};

const createAccessToken = (payload) => {
    return jwt.sign(payload, "AAAA", {
        expiresIn: "1d",
    });
};

const createRefreshToken = (payload) => {
    return jwt.sign(payload, "AAAA", {
        expiresIn: "30d",
    });
};

module.exports = authCtrl;
JavaScript
// controllers/userCtrl.js

const Users = require("../models/userModel");

const userCtrl = {
    searchUser: async (req, res) => {
        try {
            const users = await Users.find({
                username: { $regex: req.query.username },
            })
                .limit(10)
                .select("fullname username avatar");

            res.json({ users });
        } catch (err) {
            return res.status(500).json({ msg: err.message });
        }
    },

    getUser: async (req, res) => {
        try {
            const user = await Users.findById(req.params.id)
                .select("-password")
                .populate("followers following", "-password");

            if (!user) {
                return res.status(400).json({ msg: "requested user does not exist." });
            }

            res.json({ user });
        } catch (err) {
            return res.status(500).json({ msg: err.message });
        }
    },

    updateUser: async (req, res) => {
        try {
            const { avatar, fullname, mobile, address, story, website, gender } =
                req.body;
            if (!fullname) {
                return res.status(400).json({ msg: "Please add your full name." });
            }

            await Users.findOneAndUpdate(
                { _id: req.user._id },
                { avatar, fullname, mobile, address, story, website, gender }
            );

            res.json({ msg: "Profile updated successfully." });
        } catch (err) {
            return res.status(500).json({ msg: err.message });
        }
    },

    follow: async (req, res) => {
        try {
            const user = await Users.find({
                _id: req.params.id,
                followers: req.user._id,
            });
            if (user.length > 0)
                return res
                    .status(500)
                    .json({ msg: "You are already following this user." });

            const newUser = await Users.findOneAndUpdate(
                { _id: req.params.id },
                {
                    $push: {
                        followers: req.user._id,
                    },
                },
                { new: true }
            ).populate("followers following", "-password");

            await Users.findOneAndUpdate(
                { _id: req.user._id },
                { $push: { following: req.params.id } },
                { new: true }
            );

            res.json({ newUser });
        } catch (err) {
            return res.status(500).json({ msg: err.message });
        }
    },

    unfollow: async (req, res) => {
        try {
            const newUser = await Users.findOneAndUpdate(
                { _id: req.params.id },
                {
                    $pull: { followers: req.user._id },
                },
                { new: true }
            ).populate("followers following", "-password");

            await Users.findOneAndUpdate(
                { _id: req.user._id },
                { $pull: { following: req.params.id } },
                { new: true }
            );

            res.json({ newUser });
        } catch (err) {
            return res.status(500).json({ msg: err.message });
        }
    },

    suggestionsUser: async (req, res) => {
        try {
            const newArr = [...req.user.following, req.user._id];

            const num = req.query.num || 10;
            const users = await Users.aggregate([
                { $match: { _id: { $nin: newArr } } },
                { $sample: { size: Number(num) } },
                {
                    $lookup: {
                        from: "users",
                        localField: "followers",
                        foreignField: "_id",
                        as: "followers",
                    },
                },
                {
                    $lookup: {
                        from: "users",
                        localField: "following",
                        foreignField: "_id",
                        as: "following",
                    },
                },
            ]).project("-password");

            return res.json({
                users,
                result: users.length,
            });
        } catch (err) {
            return res.status(500).json({ msg: err.message });
        }
    },
};

module.exports = userCtrl;
JavaScript
// middelware/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;
JavaScript
// routes/authRoutes.js

const router = require('express').Router();
const authCtrl = require('../controllers/authCtrl');
const auth = require('../middleware/auth');

router.post('/register', authCtrl.register);
router.post("/register_admin", authCtrl.registerAdmin);
router.post("/changePassword", auth,  authCtrl.changePassword);

router.post("/login", authCtrl.login);
router.post("/admin_login", authCtrl.adminLogin);

router.post("/logout", authCtrl.logout);

router.post("/refresh_token", authCtrl.generateAccessToken);

module.exports = router;
JavaScript
// routes/userRoutes.js

const router = require('express').Router();
const auth = require('../middleware/auth');
const userCtrl = require('../controllers/userCtrl');

router.get('/search', auth, userCtrl.searchUser);

router.get('/user/:id', auth, userCtrl.getUser);

router.patch("/user", auth, userCtrl.updateUser);

router.patch("/user/:id/follow", auth, userCtrl.follow);
router.patch("/user/:id/unfollow", auth, userCtrl.unfollow);

router.get("/suggestionsUser", auth, userCtrl.suggestionsUser);

module.exports = router;


Start your app using the below command

node index.js

Steps To Setup Frontend

Step 1: Set up React frontend using the command.

npm create vite@latest client
cd client
npm install

Step 2: Install the required dependencies.

npm install @mui/material @emotion/react @emotion/styled react-router-dom @mui/icons-material axios

Project Structure

00
Project Structure

Dependencies

"dependencies": {
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@mui/icons-material": "^6.1.0",
"@mui/material": "^6.1.0",
"axios": "^1.7.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.53.0",
"react-router-dom": "^6.26.2"
}

Frontend Example:

JavaScript
// App.jsx

import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import UserProfile from './components/UserProfile.jsx';
import Register from './components/Register';
import Login from './components/Login';
import Navbar from './components/Navbar';
import HomePage from './pages/HomePage';

const App = () => {
    return (
        <Router>
            <Navbar />
            <Routes>
                <Route path="/" element={<HomePage />} />
                <Route path="/register" element={<Register />} />
                <Route path="/login" element={<Login />} />
                <Route path="/user/:id" element={<UserProfile />} />
            </Routes>
        </Router>
    );
};

export default App;
JavaScript
// components/Register.jsx

import { Box, Button, MenuItem, TextField, Typography } from "@mui/material";
import axios from "axios";
import React from "react";
import { useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";

const Register = () => {
    const {
        register,
        handleSubmit,
        watch,
        formState: { errors },
    } = useForm();
    const navigate = useNavigate();

    const onSubmit = async (data) => {
        try {
            await axios.post("http://localhost:3001/api/register_admin", data, {
                withCredentials: true,
            });
            navigate("/login");
        } catch (err) {
            console.log(err.response.data.msg);
        }
    };

    return (
        <Box
            component="form"
            onSubmit={handleSubmit(onSubmit)}
            sx={{ maxWidth: 400, margin: "auto" }}
        >
            <Typography variant="h4" gutterBottom>
                Register
            </Typography>
            <TextField
                {...register("fullname", { required: true })}
                label="Full Name"
                variant="outlined"
                fullWidth
                margin="normal"
            />
            {errors.fullname && (
                <Typography color="error">Full Name is required</Typography>
            )}

            <TextField
                {...register("username", { required: true })}
                label="Username"
                variant="outlined"
                fullWidth
                margin="normal"
            />
            {errors.username && (
                <Typography color="error">Username is required</Typography>
            )}

            <TextField
                {...register("email", { required: true })}
                label="Email"
                variant="outlined"
                fullWidth
                margin="normal"
            />
            {errors.email && <Typography color="error">Email is required</Typography>}

            <TextField
                {...register("password", { required: true, minLength: 6 })}
                label="Password"
                variant="outlined"
                fullWidth
                margin="normal"
                type="password"
            />
            {errors.password && (
                <Typography color="error">
                    Password must be at least 6 characters
                </Typography>
            )}

            <TextField
                {...register("gender")}
                label="Gender"
                variant="outlined"
                fullWidth
                margin="normal"
                select
            >
                <MenuItem value="male">Male</MenuItem>
                <MenuItem value="female">Female</MenuItem>
            </TextField>

            <Button type="submit" variant="contained" color="primary" fullWidth>
                Register
            </Button>
        </Box>
    );
};

export default Register;
JavaScript
// components/Login.jsx

import { Box, Button, TextField, Typography } from "@mui/material";
import axios from "axios";
import React from "react";
import { useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";

const Login = () => {
    const {
        register,
        handleSubmit,
        formState: { errors },
    } = useForm();
    const navigate = useNavigate();

    const onSubmit = async (data) => {
        try {
            const res = await axios.post("http://localhost:3001/api/login", data);
            localStorage.setItem("token", res.data.access_token);
            localStorage.setItem("user", JSON.stringify(res.data.user));
            console.log("res", res.data.user._id);
            navigate(`/user/${res.data.user._id}`);
        } catch (err) {
            console.log(err.response.data.msg);
        }
    };

    return (
        <Box
            component="form"
            onSubmit={handleSubmit(onSubmit)}
            sx={{ maxWidth: 400, margin: "auto" }}
        >
            <Typography variant="h4" gutterBottom>
                Login
            </Typography>
            <TextField
                {...register("email", { required: true })}
                label="Email"
                variant="outlined"
                fullWidth
                margin="normal"
            />
            {errors.email && <Typography color="error">Email is required</Typography>}

            <TextField
                {...register("password", { required: true })}
                label="Password"
                variant="outlined"
                fullWidth
                margin="normal"
                type="password"
            />
            {errors.password && (
                <Typography color="error">Password is required</Typography>
            )}

            <Button type="submit" variant="contained" color="primary" fullWidth>
                Login
            </Button>
        </Box>
    );
};

export default Login;
JavaScript
//components/UserProfile.jsx

import {
    Email,
    LocationOn,
    People,
    PersonAdd,
    PersonRemove,
    Phone,
    Web
} from "@mui/icons-material";
import {
    Avatar,
    Box,
    Button,
    Divider,
    Grid,
    IconButton,
    Paper,
    Typography
} from "@mui/material";
import { styled } from "@mui/material/styles";
import axios from "axios";
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";

const ProfileContainer = styled(Box)(
    ({ theme }) => ({
        maxWidth: "800px",
        margin: "20px auto",
        padding: theme.spacing(3),
        backgroundColor: "#fff",
        borderRadius: theme.shape.borderRadius,
        boxShadow: theme.shadows[3],
    }));

const UserProfile = () => {
    const { id } = useParams();
    const [user, setUser] = useState(null);
    const [isFollowing, setIsFollowing] = useState(false);
    const token = localStorage.getItem("token");

    useEffect(() => {
        const fetchUser = async () => {
            try {
                const res = await axios.get(
                    `http://localhost:3001/api/user/$%7Bid%7D%60,
                    {
                        headers: {
                            Authorization:
                                `Bearer ${token}`,
                        },
                    });
                setUser(res.data.user);
                // Assuming current user ID is in
                // localStorage or context
                const currentUserId
                    = JSON.parse(
                        localStorage.getItem("user"))
                        ?._id;
                setIsFollowing(
                    res.data.user?.followers.includes(
                        currentUserId));
            }
            catch (err) {
                console.log(err.response.data.msg);
            }
        };

        fetchUser();
    }, [id]);

    const handleFollow = async () => {
        try {
            if (isFollowing) {
                await axios.patch(
                    `/api/users/${id}/unfollow`, {}, {
                    headers: {
                        Authorization:
                            `Bearer ${token}`
                    }
                });
            }
            else {
                await axios.patch(
                    `/api/users/${id}/follow`, {}, {
                    headers: {
                        Authorization:
                            `Bearer ${token}`
                    }
                });
            }
            setIsFollowing(!isFollowing);
        }
        catch (err) {
            console.log(err.response.data.msg);
        }
    };

    return user ? (
        <ProfileContainer>
            {/* Profile Header */}
            <Grid container spacing={2} alignItems="center">
                <Grid item>
                    <Avatar src={user.avatar} alt={user?.fullname} 
                    sx={{ width: 100, height: 100 }} />
                </Grid>
                <Grid item>
                    <Typography variant="h5">{user?.fullname}</Typography>
                    <Typography variant="subtitle1">@{user?.username}</Typography>
                    <Typography color="textSecondary">{user?.role}</Typography>
                    <Button
                        variant="contained"
                        color={isFollowing ? 'secondary' : 'primary'}
                        startIcon={isFollowing ? <PersonRemove /> : <PersonAdd />}
                        onClick={handleFollow}
                        sx={{
                            mt: 1
                        }}
                    >
                        {isFollowing ? "Unfollow" : "Follow"}
                    </Button>
                </Grid>
            </Grid>

            <Divider sx={{ my: 3 }} />

            {/* Profile Details */}
            <Grid container spacing={2}>
                <Grid item xs={12} sm={6}>
                    <Typography variant="body1">
                        <Email /> {user?.email}
                    </Typography>
                    {user?.mobile && (
                        <Typography variant="body1">
                            <Phone /> {user?.mobile}
                        </Typography>
                    )}
                    {user?.address && (
                        <Typography variant="body1">
                            <LocationOn /> {user?.address}
                        </Typography>
                    )}
                    {user?.website && (
                        <Typography variant="body1">
                            <Web /> <a href={user?.website} target="_blank" 
                            rel="noopener noreferrer">{user?.website}</a>
                        </Typography>
                    )
                    }
                </Grid>
                <Grid item xs={12} sm={6}>
                    <Typography variant="body1">
                        <People /> {user?.followers?.length} Followers
                    </Typography>
                    <Typography variant="body1">
                        <People /> {user?.following?.length} Following
                    </Typography>
                    {user?.gender && (
                        <Typography variant="body1">Gender:
                         {user?.gender}</Typography>
                    )
                    }
                    <Typography variant="body1">Joined on:
                        {new Date(user?.createdAt)
                            .toLocaleDateString()}<
                /Typography>
                </Grid>
            </Grid>

            <Divider sx={{ my: 3 }} />

            {/* User Story or Bio */}
            {
                user?.story
                && (<Box><Typography variant="h6">About Me<
                    /Typography>
                    <Typography>{user?.story}</Typography>
                </Box>
                )}

            {/
                * Saved Posts Section
                     * /}
            <Divider sx={{ my: 3 }} />
            <Box>
                <Typography variant="h6">Saved Posts<
                        /Typography>
                    {user?.saved?.length > 0 ? (
                        <Typography>{user?.saved?.length} saved posts</Typography>) :
                        (<Typography>No saved posts<
                        /Typography>
        )}
                        </Box>
                     </ProfileContainer>
                ) : (
                <Typography>Loading...</Typography>);
        };

export default UserProfile;


Start your application using the below command:

npm run dev

Output:


Similar Reads