Freelancer Portfolio Builder Application using MERN Stack
Last Updated :
01 Apr, 2024
In today's digital age, having a professional portfolio is essential for freelancers to showcase their skills and attract potential clients. In this article, we'll dive into the process of building a Freelancer Portfolio Builder using the MERN (MongoDB, Express.js, React.js, Node.js) stack. This project will allow freelancers to create, edit, and manage their portfolios effectively.
Output Preview: Let us have a look at how the final output will look like.

Prerequisites:
Approach to create Freelancer Portfolio Builder Application:
User-friendly Interface:
- The user interface will be designed to provide a seamless experience for users to add, edit, and delete portfolios.
- Use modern design principles and UI frameworks like Material UI or Bootstrap for a clean and responsive layout.
- Implement intuitive navigation and user interactions to ensure ease of use.
Real-time Updates using React Components:
- Utilize React's component-based architecture to create modular and reusable UI components.
- Implement real-time updates using React Hooks (e.g., useState, useEffect) or Context API for managing state and triggering re-renders when data changes.
- Use WebSocket or a real-time database like Firebase Firestore for instant updates if required.
Integration with MongoDB:
- Set up a MongoDB database to store portfolio data.
- Use Mongoose (a MongoDB object modeling tool for Node.js) to define schemas, models, and perform database operations.
- Establish a connection to MongoDB using mongoose.connect in the backend server.
RESTful API Endpoints:
- Create RESTful API endpoints in Node.js using Express.js to handle CRUD operations (Create, Read, Update, Delete) for portfolios.
- Define routes for handling HTTP requests such as GET, POST, PUT, DELETE for portfolios.
- Use middleware like body-parser or express.json to parse incoming request bodies as JSON.
Steps to Create the Frontend:
Step 1: Develop frontend using React.js for the user interface.
mkdir freelancer-portfolio-builder-frontend
cd freelancer-portfolio-builder-frontend
npx create-react-app .
Step 2: Connect the frontend and backend using Axios for making API requests.
Project Structure(Frontend):
The updated dependencies in package.json file will look like:
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.8",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}
Example: Below is the frontend code:
JavaScript
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const App = () => {
const [portfolios, setPortfolios] = useState([]);
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [skills, setSkills] = useState('');
const [githubLink, setGithubLink] = useState('');
useEffect(() => {
axios.get('http://localhost:5000/api/portfolios')
.then(res => setPortfolios(res.data))
.catch(err => console.error(err));
}, []);
const addPortfolio = (newPortfolio) => {
setPortfolios([...portfolios, newPortfolio]);
};
const handleSubmit = (e) => {
e.preventDefault();
const newPortfolio =
{
title, description,
skills: skills.split(','), githubLink
};
axios.post('http://localhost:5000/api/portfolios', newPortfolio)
.then(res => {
alert(res.data.message);
addPortfolio(newPortfolio);
setTitle('');
setDescription('');
setSkills('');
setGithubLink('');
})
.catch(err => console.error(err));
};
const handleDelete = (id) => {
axios.delete(`http://localhost:5000/api/portfolios/${id}`)
.then(res => {
alert(res.data.message);
setPortfolios(portfolios.filter(p => p._id !== id));
})
.catch(err => console.error(err));
};
return (
<div className="App"
style={
{
maxWidth: '800px', margin: '0 auto',
padding: '20px'
}}>
<h1 style={{ textAlign: 'center' }}>
Freelancer Portfolio Builder
</h1>
<div style={{ marginBottom: '20px' }}>
<h2>Add Portfolio</h2>
<form onSubmit={handleSubmit}
style={
{
display: 'flex',
flexDirection: 'column',
gap: '10px'
}}>
<input type="text"
placeholder="Title"
value={title}
onChange={
(e) =>
setTitle(e.target.value)
} required style={{ padding: '5px' }} />
<input type="text"
placeholder="Description"
value={description}
onChange={
(e) =>
setDescription(e.target.value)
}
required
style={{ padding: '5px' }} />
<input type="text"
placeholder="Skills (comma-separated)"
value={skills}
onChange={(e) => setSkills(e.target.value)}
required
style={{ padding: '5px' }} />
<input type="text" placeholder="Github Link"
value={githubLink}
onChange={(e) => setGithubLink(e.target.value)}
required style={{ padding: '5px' }} />
<button type="submit"
style={
{
padding: '5px',
backgroundColor: '#007bff',
color: '#fff',
border: 'none',
cursor: 'pointer'
}}>Add Portfolio</button>
</form>
</div>
<div>
<h2>Portfolios</h2>
{portfolios.map(portfolio => (
<div key={portfolio._id}
style={
{
border: '1px solid #ccc',
borderRadius: '5px',
padding: '10px', marginBottom: '10px'
}}>
<h3>{portfolio.title}</h3>
<p>{portfolio.description}</p>
<p>Skills: {portfolio.skills.join(', ')}</p>
<p>Github Link: {portfolio.githubLink}</p>
<button
onClick={
() =>
handleDelete(portfolio._id)
}
style={
{
padding: '5px',
backgroundColor: 'red',
color: '#fff', border: 'none',
cursor: 'pointer'
}}>Delete</button>
</div>
))}
</div>
</div>
);
};
export default App;
Steps to Create Backend:
Step 1: Initialize a new Node.js project and install necessary dependencies.
mkdir freelancer-portfolio-builder
cd freelancer-portfolio-builder
npm init -y
npm install express mongoose dotenv cors
Step 2: Create backend API routes using Express.js for CRUD operations.
Project Structure(Backend):

The updated dependencies in package.json file will look like:
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.3",
"mongoose": "^8.2.2"
}
Example: Below is the backend code:
JavaScript
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const app = express();
const PORT = process.env.PORT || 5000;
// Middleware
app.use(cors());
app.use(express.json());
// MongoDB connection
mongoose.connect('mongodb://localhost:27017/freelancerportfolio', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const connection = mongoose.connection;
connection.once('open', () => {
console.log('MongoDB connection established successfully');
});
// MongoDB Model
const portfolioSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
skills: {
type: [String],
required: true
},
githubLink: {
type: String,
required: true
}
});
const Portfolio = mongoose.model('Portfolio', portfolioSchema);
// API Routes
app.get('/api/portfolios', async (req, res) => {
try {
const portfolios = await Portfolio.find();
res.json(portfolios);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.post('/api/portfolios', async (req, res) => {
try {
const { title, description, skills, githubLink } = req.body;
const newPortfolio = new Portfolio({
title,
description,
skills,
githubLink
});
await newPortfolio.save();
res.json({ message: 'Portfolio created successfully' });
} catch (err) {
res.status(400).json({ error: err.message });
}
});
app.put('/api/portfolios/:id', async (req, res) => {
try {
const { title, description, skills, githubLink } = req.body;
await Portfolio.findByIdAndUpdate(req.params.id, {
title,
description,
skills,
githubLink
});
res.json({ message: 'Portfolio updated successfully' });
} catch (err) {
res.status(400).json({ error: err.message });
}
});
app.delete('/api/portfolios/:id', async (req, res) => {
try {
await Portfolio.findByIdAndDelete(req.params.id);
res.json({ message: 'Portfolio deleted successfully' });
} catch (err) {
res.status(400).json({ error: err.message });
}
});
// Start the server
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Steps to Run the Application:
- Start the Node.js server using
node server.js
- Navigate to the frontend directory and start the React development server using
npm start
- Access the application in your web browser at http://localhost:3000
Output:


Similar Reads
Community Marketplace App using MERN Stack Building a community Marketplace App will help you understand the foundational concepts of full-stack development using the MERN(MongoDB, ExpressJS, React, NodeJS) stack. This tutorial will guide you to set up the backend server for the project and demonstrate the integration of frontend functionali
6 min read
Todo List Application using MERN Todo list web application using MERN stack is a project that basically implements basic CRUD operation using MERN stack (MongoDB, Express JS, Node JS, React JS). The users can read, add, update, and delete their to-do list in the table using the web interface. The application gives a feature to the
8 min read
Crafting a stunning CRUD Application with MERN stack Creating a CRUD (Create, Read, Update, Delete) application using the MERN stack is an excellent way to build dynamic web applications with JavaScript technologies. The MERN stack consists of MongoDB, Express.js, React, and Node.js, offering a full-stack JavaScript solution for building modern web ap
5 min read
Notes Maker App using MERN Stack The "Notes Maker" project is an helpful web application designed to help users effectively create, manage, and organize their notes. In this article we are utilizing the MERN (MongoDB, Express, React, Node) Stack, to build a notes maker application that provides a seamless user experience for note-t
6 min read
Create a Portfolio App using React-Native In this article, we are going to Create a portfolio app using React Native. The portfolio app is a simple application that is a collection of a person's qualifications, achievements, work samples, and other relevant materials. It is used to demonstrate one's abilities and suitability for a particula
5 min read