DEV Community

Cover image for How I Built a Dynamic Admin Panel for My MERN Stack Portfolio
Adithyan G
Adithyan G

Posted on

How I Built a Dynamic Admin Panel for My MERN Stack Portfolio

As a developer, building a portfolio isn’t just about showcasing projects — it’s about demonstrating how you can create flexible, maintainable web applications. For my portfolio website, I wanted more than a static showcase. I needed a dynamic Admin Panel that would allow me to easily manage my projects, skills, profile details, and resume — all without touching the code every time.

In this post, I’ll walk you through how I built a dynamic admin dashboard using the MERN stack (MongoDB, Express, React, Node.js) that powers my portfolio backend and frontend. This admin panel makes managing my portfolio content a breeze, and I’ll share the key architecture decisions, tools, and lessons learned.


Why Build an Admin Panel for Your Portfolio?

At first, I just had a simple React portfolio displaying static content. But soon, every update required code changes and redeployments, which was time-consuming and error-prone.

By building an admin dashboard, I achieved:

  • Real-time content updates: Add, update, or delete projects, skills, and other profile data via a UI.
  • Secure access: Only I can log in to manage content with JWT authentication.
  • Scalability: Easily extend with new sections or data types without modifying the frontend.
  • Better organization: Store portfolio content in a database instead of hardcoded files.

Tech Stack Overview

Here’s the stack I used to build the admin panel and the portfolio backend:

  • Frontend: React with functional components and hooks for building the admin UI.
  • Backend: Node.js + Express REST APIs for handling CRUD operations.
  • Database: MongoDB with Mongoose for schema modeling and data storage.
  • Authentication: JWT (JSON Web Tokens) to secure admin routes.
  • File Storage: Cloudinary for managing image uploads (profile pics, project images).
  • Styling: CSS modules and some UI libraries for clean layouts.

Key Features of the Admin Panel

The dashboard lets me manage:

  • Profile Section: Update bio, profile picture, contact info.
  • Skills: Add, update, or remove programming skills dynamically.
  • Projects: CRUD operations for projects including title, description, images, links.
  • Resume: Upload and update resume files.
  • Authentication: Secure login with email and password, protected routes using JWT.

How It Works — Architecture Breakdown

1. Backend API Design

I created RESTful APIs to handle CRUD actions for all portfolio data.

Example routes:

  • GET /api/projects — fetch all projects
  • POST /api/projects — add a new project
  • PUT /api/projects/:id — update project by ID
  • DELETE /api/projects/:id — delete project by ID

The APIs validate requests and interact with MongoDB collections using Mongoose models. I also implemented authentication middleware to protect sensitive endpoints.

2. Authentication with JWT

To secure the admin panel, I built a login system with:

  • Password hashing using bcrypt.
  • JWT token generation on login.
  • Protected routes in both frontend and backend that require a valid token.

3. React Admin Dashboard UI

Using React hooks and axios for API calls, I built components for:

  • Login Page: Authenticate and store JWT in localStorage.
  • Dashboard: Overview of portfolio content.
  • Forms: Controlled forms for adding/editing skills and projects.
  • File Uploads: Integrated Cloudinary API for image and resume uploads.

I also used conditional rendering and loading states for smooth UX.


Code Snippet — Example: Fetching Projects in Admin Panel

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

const AdminProjects = () => {
  const [projects, setProjects] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchProjects = async () => {
      try {
        const token = localStorage.getItem("authToken");
        const response = await axios.get("/api/projects", {
          headers: { Authorization: `Bearer ${token}` },
        });
        setProjects(response.data);
      } catch (error) {
        console.error("Error fetching projects:", error);
      } finally {
        setLoading(false);
      }
    };
    fetchProjects();
  }, []);

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

  return (
    <div>
      <h2>Manage Projects</h2>
      {projects.length === 0 ? (
        <p>No projects found.</p>
      ) : (
        projects.map((project) => (
          <div key={project._id}>
            <h3>{project.title}</h3>
            <p>{project.description}</p>
            {/* Buttons for Edit/Delete can go here */}
          </div>
        ))
      )}
    </div>
  );
};

export default AdminProjects;
Enter fullscreen mode Exit fullscreen mode

Challenges & Lessons Learned

Handling File Uploads: Uploading images and resumes required integrating Cloudinary's API with signed URLs and managing upload states.

State Management: Keeping the admin UI in sync with backend data, especially with real-time CRUD updates, needed careful use of React state and effects.

Authentication Flow: Securing routes and managing JWT tokens was critical to avoid unauthorized access.

UX Considerations: Designing simple but effective forms and feedback messages improved admin usability.

Final Thoughts

Building a dynamic admin panel transformed my portfolio from a static webpage into a powerful content management system I can control without redeploying. It gave me hands-on experience with full-stack development, API design, secure authentication, and file management — all essential skills for a modern web developer.

If you’re planning to build or improve your portfolio, I highly recommend adding an admin dashboard. It not only shows your technical skills but also saves you a ton of time and effort in the long run.

If you want to check out my portfolio in action, feel free to visit:
https://adithyan-phi.vercel.app

Got questions or want to share your experience? Drop a comment below!

Happy coding!

Top comments (0)