DEV Community

Cover image for Building a Full Stack Portfolio with Dashboard (MERN Stack: JavaScript, CSS, HTML)
Code WithDhanian
Code WithDhanian

Posted on

Building a Full Stack Portfolio with Dashboard (MERN Stack: JavaScript, CSS, HTML)

In this comprehensive guide, we will create a full-stack portfolio application with a dashboard. Using the MERN stack (MongoDB, Express, React, and Node.js), we’ll also incorporate Redux for state management. This application will showcase portfolio data, including personal details, skills, and projects. Let’s dive in!

1. Backend Setup

The backend of our application will use Node.js with Express.js for API routes and MongoDB for the database.

Setting up the Express Server
We’ll begin by setting up an Express server to handle our API routes and manage communication with the MongoDB database.

const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
require('dotenv').config();

const app = express();
app.use(cors());
app.use(express.json());

mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log('MongoDB connected'))
    .catch(err => console.error(err));

const portfolioRoutes = require('./routes/portfolioRoutes');
app.use('/api/portfolio', portfolioRoutes);

app.listen(5000, () => console.log(`Server running on port 5000`));
Enter fullscreen mode Exit fullscreen mode

Explanation

  • CORS Middleware: Enables cross-origin requests, allowing the frontend and backend to communicate seamlessly.
  • MongoDB Connection: The mongoose.connect() function connects to the MongoDB database using the URI from .env.
  • Routes: API routes are defined in portfolioRoutes and prefixed with /api/portfolio.

2. Portfolio Model

Define a Mongoose schema and model for storing portfolio details, such as name, email, skills, and projects.

const mongoose = require('mongoose');

const PortfolioSchema = new mongoose.Schema({
    name: { type: String, required: true },
    email: { type: String, required: true },
    skills: [String],
    projects: [
        {
            title: "String,"
            description: "String,"
            link: String,
        },
    ],
}, { timestamps: true });

module.exports = mongoose.model('Portfolio', PortfolioSchema);
Enter fullscreen mode Exit fullscreen mode

Explanation

  • Schema Definition: Defines the structure of portfolio data with fields for name, email, skills, and an array of projects.
  • Timestamps: Automatically tracks when each document is created or updated.

** 3. API Routes**

Set up routes to handle CRUD operations for portfolio data.

const express = require('express');
const Portfolio = require('../models/Portfolio');
const router = express.Router();

router.get('/', async (req, res) => {
    const portfolios = await Portfolio.find();
    res.json(portfolios);
});

router.post('/', async (req, res) => {
    const portfolio = new Portfolio(req.body);
    await portfolio.save();
    res.status(201).json(portfolio);
});

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

Explanation

  • GET Route: Fetches all portfolio entries from the database.
  • POST Route: Allows new portfolio entries to be added.

4. Frontend Setup

Setting up the React Application
Run the following commands to set up a React app and install necessary dependencies.

npx create-react-app client
cd client
npm install axios redux react-redux @reduxjs/toolkit react-router-dom
Enter fullscreen mode Exit fullscreen mode

Explanation

  • Axios: Used for making API requests.
  • Redux: Manages the state of the application.

5. Redux Slice

Create a slice for managing portfolio data and API interactions.

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';

export const fetchPortfolios = createAsyncThunk('portfolio/fetch', async () => {
    const response = await axios.get('http://localhost:5000/api/portfolio');
    return response.data;
});

const portfolioSlice = createSlice({
    name: 'portfolio',
    initialState: { data: [], loading: false },
    extraReducers: (builder) => {
        builder
            .addCase(fetchPortfolios.pending, (state) => { state.loading = true; })
            .addCase(fetchPortfolios.fulfilled, (state, action) => {
                state.data = action.payload;
                state.loading = false;
            });
    },
});

export default portfolioSlice.reducer;
Enter fullscreen mode Exit fullscreen mode

Explanation

  • createAsyncThunk: Handles asynchronous API calls and manages loading states.
  • Reducers: Updates the state based on API response.

6. Dashboard Component

Create a dashboard to display portfolio data fetched from the backend.

import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchPortfolios } from '../features/portfolioSlice';

const Dashboard = () => {
    const dispatch = useDispatch();
    const { data, loading } = useSelector(state => state.portfolio);

    useEffect(() => {
        dispatch(fetchPortfolios());
    }, [dispatch]);

    if (loading) return <p>Loading...</p>;

    return (
        <div>
            <h1>Portfolio Dashboard</h1>
            {data.map(portfolio => (
                <div key={portfolio._id}>
                    <h3>{portfolio.name}</h3>
                    <p>{portfolio.email}</p>
                    <ul>
                        {portfolio.skills.map((skill, idx) => (
                            <li key={idx}>{skill}</li>
                        ))}
                    </ul>
                </div>
            ))}
        </div>
    );
};

export default Dashboard;
Enter fullscreen mode Exit fullscreen mode

Explanation

  • useEffect: Fetch portfolio data when the component loads.
  • Conditional Rendering: Displays a loading message while data is being fetched.

** 7. Profile with Image**

Add a profile image and application title to the frontend.

App.js

import React from 'react';
import Dashboard from './components/Dashboard';
import './App.css';

const App = () => {
    return (
        <div className="App">
            <header className="App-header">
                <img
                    src="https://i.ibb.co/ZNGx9M4/developer.jpg"
                    alt="Profile"
                    className="profile-image"
                />
                <h1>My Full Stack Portfolio</h1>
            </header>
            <Dashboard />
        </div>
    );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

App.css

.App-header {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin: 20px;
}

.profile-image {
    border-radius: 50%;
    width: 150px;
    height: 150px;
    margin-bottom: 20px;
}
Enter fullscreen mode Exit fullscreen mode

Explanation

  • Profile Section: Displays a profile image and title.
  • CSS: Styles the header for better visual appeal.

8. Running the Application

Start Backend

node server.js
Enter fullscreen mode Exit fullscreen mode

Start Frontend

npm start
Enter fullscreen mode Exit fullscreen mode

Output

  • The application will display a profile image, a title, and a dashboard listing portfolio details.

Conclusion

By following this guide, you’ve built a full-stack portfolio application with a dashboard using the MERN stack. This project demonstrates key concepts in backend development, API creation, frontend development with React, and state management with Redux. You can further expand the application by adding user authentication, advanced styling, or deploying it to a cloud platform. Happy coding!

Top comments (0)