DEV Community

CodeWithDhanian
CodeWithDhanian

Posted on

Building a Full-Stack Portfolio Using the MERN Stack-A Step-by-Step Guide

As a software developer and tutor, creating a full-stack portfolio is an excellent way to showcase your skills, projects, and expertise. In this article, I’ll walk you through how to build a modern, responsive portfolio using the MERN stack (MongoDB, Express.js, React.js, and Node.js). By the end of this guide, you’ll have a fully functional portfolio that you can deploy and share with the world.


Why the MERN Stack?

The MERN stack is a popular choice for full-stack development because:

  1. JavaScript Everywhere: You use JavaScript for both frontend and backend development, making it easier to maintain.
  2. Scalability: MongoDB and Node.js are highly scalable, making them suitable for projects of any size.
  3. Community Support: With a large and active community, you’ll find plenty of resources and tutorials to help you along the way.

Portfolio Features

Our portfolio will include the following features:

  1. Homepage: A brief introduction and overview of your skills.
  2. Projects Section: A showcase of your work with descriptions and links.
  3. Blog/Tutorials Section: A space to share your knowledge as a tutor.
  4. Contact Form: A way for visitors to reach out to you.

Step 1: Setting Up the Project

1.1 Create the Project Structure

Start by creating a project folder and setting up the backend and frontend:

mkdir dhanian-portfolio
cd dhanian-portfolio

# Backend setup
mkdir backend
cd backend
npm init -y
npm install express mongoose cors dotenv

# Frontend setup
cd ..
npx create-react-app frontend
cd frontend
npm install axios react-router-dom
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Enter fullscreen mode Exit fullscreen mode

1.2 Backend Setup

The backend will handle the contact form submissions and serve as the API for your portfolio.

1.2.1 Create the Backend Structure

backend/
├── models/
├── routes/
├── controllers/
├── .env
├── server.js
Enter fullscreen mode Exit fullscreen mode

1.2.2 Set Up the Server

In backend/server.js, add the following code:

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

dotenv.config();
const app = express();
const PORT = process.env.PORT || 5000;

// Middleware
app.use(cors());
app.use(express.json());

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

// Routes
app.get('/', (req, res) => {
  res.send('Backend is running');
});

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

1.2.3 Add a Contact Model

Create a Contact model in backend/models/Contact.js:

const mongoose = require('mongoose');

const ContactSchema = new mongoose.Schema({
  name: { type: String, required: true },
  email: { type: String, required: true },
  message: { type: String, required: true },
});

module.exports = mongoose.model('Contact', ContactSchema);
Enter fullscreen mode Exit fullscreen mode

1.2.4 Add a Contact Route

Create a route for the contact form in backend/routes/contact.js:

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

router.post('/', async (req, res) => {
  const { name, email, message } = req.body;
  try {
    const newContact = new Contact({ name, email, message });
    await newContact.save();
    res.status(201).json({ message: 'Message sent successfully' });
  } catch (err) {
    res.status(500).json({ error: 'Server error' });
  }
});

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

1.2.5 Update the Server to Use the Contact Route

In backend/server.js, add:

const contactRoutes = require('./routes/contact');
app.use('/api/contact', contactRoutes);
Enter fullscreen mode Exit fullscreen mode

1.3 Frontend Setup

The frontend will be built using React.js and styled with Tailwind CSS.

1.3.1 Configure Tailwind CSS

Update frontend/tailwind.config.js:

module.exports = {
  content: ['./src/**/*.{js,jsx,ts,tsx}'],
  theme: {
    extend: {},
  },
  plugins: [],
};
Enter fullscreen mode Exit fullscreen mode

Add Tailwind to frontend/src/index.css:

@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

1.3.2 Create the Homepage

In frontend/src/components/Home.js, add:

import React from 'react';

const Home = () => {
  return (
    <div className="min-h-screen bg-gray-100 flex items-center justify-center">
      <h1 className="text-4xl font-bold text-blue-600">Welcome to Dhanian's Portfolio</h1>
    </div>
  );
};

export default Home;
Enter fullscreen mode Exit fullscreen mode

1.3.3 Set Up Routing

In frontend/src/App.js, add:

import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Home from './components/Home';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
      </Routes>
    </Router>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Step 2: Connect Frontend and Backend

2.1 Create a Contact Form

In frontend/src/components/Contact.js, add:

import React, { useState } from 'react';
import axios from 'axios';

const Contact = () => {
  const [formData, setFormData] = useState({ name: '', email: '', message: '' });

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      await axios.post('http://localhost:5000/api/contact', formData);
      alert('Message sent successfully');
    } catch (err) {
      alert('Error sending message');
    }
  };

  return (
    <form onSubmit={handleSubmit} className="max-w-md mx-auto mt-10">
      <input
        type="text"
        placeholder="Name"
        value={formData.name}
        onChange={(e) => setFormData({ ...formData, name: e.target.value })}
        className="w-full p-2 mb-4 border rounded"
      />
      <input
        type="email"
        placeholder="Email"
        value={formData.email}
        onChange={(e) => setFormData({ ...formData, email: e.target.value })}
        className="w-full p-2 mb-4 border rounded"
      />
      <textarea
        placeholder="Message"
        value={formData.message}
        onChange={(e) => setFormData({ ...formData, message: e.target.value })}
        className="w-full p-2 mb-4 border rounded"
      />
      <button type="submit" className="w-full p-2 bg-blue-600 text-white rounded">
        Send
      </button>
    </form>
  );
};

export default Contact;
Enter fullscreen mode Exit fullscreen mode

2.2 Add the Contact Route to App.js

Update frontend/src/App.js:

import Contact from './components/Contact';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </Router>
  );
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Deploy the Portfolio

  1. Backend: Deploy to Render or Heroku.
  2. Frontend: Deploy to Vercel or Netlify.

Conclusion

By following this guide, you’ve built a full-stack portfolio using the MERN stack. This portfolio not only showcases your skills but also demonstrates your ability to build modern, scalable web applications. Feel free to expand it by adding more features, such as authentication, a blog section, or even a dark mode toggle.

Top comments (0)