Build a Simple Next.js Interactive Portfolio App

Written by

in

In today’s digital landscape, a personal portfolio website is more than just a resume; it’s your online identity. It’s the first impression you make on potential employers, clients, and collaborators. A well-crafted portfolio showcases your skills, projects, and personality, making it easier for people to understand what you do and why they should work with you. This tutorial guides you through building a simple, yet effective, interactive portfolio application using Next.js, a powerful React framework.

Why Build a Portfolio with Next.js?

Next.js offers several advantages for building a portfolio website:

  • Server-Side Rendering (SSR) and Static Site Generation (SSG): Improve SEO and initial load times, crucial for user experience.
  • React-Based: Leverage the flexibility and component-based architecture of React.
  • Easy Routing: Simplified navigation with its built-in file-system-based routing.
  • Optimized Performance: Features like image optimization and code splitting lead to faster and more efficient websites.
  • Developer Experience: Features like hot reloading and a streamlined development process make it a joy to work with.

Prerequisites

Before we begin, ensure you have the following installed:

  • Node.js and npm (or yarn): Required to run JavaScript-based projects.
  • A Code Editor: Visual Studio Code, Sublime Text, or any editor of your choice.
  • Basic Understanding of HTML, CSS, and JavaScript: You should be familiar with the basics.

Step-by-Step Guide

1. Setting Up Your Next.js Project

Let’s start by creating a new Next.js project. Open your terminal and run the following command:

npx create-next-app portfolio-app

This command creates a new directory called portfolio-app with the basic structure of a Next.js project. Navigate into this directory:

cd portfolio-app

Now, start the development server:

npm run dev

Open your browser and go to http://localhost:3000. You should see the default Next.js welcome page.

2. Project Structure and File Organization

Next.js follows a structured file organization. Here’s a basic overview of the key directories and files we’ll be working with:

  • pages/: This directory is the heart of your routing. Each file inside pages/ becomes a route. For example, pages/index.js maps to the root path (/).
  • components/: Where you’ll store your reusable React components (e.g., navigation bar, project cards, etc.).
  • styles/: Place your CSS or styling files here.
  • public/: Contains static assets like images, fonts, and other media.
  • package.json: Contains project dependencies and scripts.

3. Creating the Basic Layout

Let’s create a basic layout for our portfolio. We’ll start by creating a Layout component to handle the common elements of the website, such as the header and footer.

Create a new file components/Layout.js and add the following code:

import Head from 'next/head';
import styles from '../styles/Layout.module.css';

function Layout({ children }) {
  return (
    <div className={styles.container}>
      <Head>
        <title>Your Name - Portfolio</title>
        <meta name="description" content="Your portfolio website" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <header className={styles.header}>
        <h1>Your Name</h1>
        <nav>
          <a href="/">About</a> | <a href="/projects">Projects</a> | <a href="/contact">Contact</a>
        </nav>
      </header>

      <main className={styles.main}>
        {children}
      </main>

      <footer className={styles.footer}>
        <p>&copy; {new Date().getFullYear()} Your Name. All rights reserved.</p>
      </footer>
    </div>
  );
}

export default Layout;

Create a file styles/Layout.module.css and add some basic styling (you can customize this to your liking):

.container {
  min-height: 100vh;
  padding: 0 0.5rem;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.header {
  width: 100%;
  padding: 1rem 0;
  text-align: center;
  border-bottom: 1px solid #eaeaea;
}

.main {
  padding: 5rem 0;
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.footer {
  width: 100%;
  height: 50px;
  border-top: 1px solid #eaeaea;
  display: flex;
  justify-content: center;
  align-items: center;
}

Now, let’s use the Layout component in our pages. Open pages/index.js and modify it:

import Layout from '../components/Layout';
import styles from '../styles/Home.module.css';

export default function Home() {
  return (
    <Layout>
      <div className={styles.container}>
        <h2>About Me</h2>
        <p>Write a brief introduction about yourself here.</p>
      </div>
    </Layout>
  );
}

Also, create a file styles/Home.module.css and add some styling:

.container {
  text-align: center;
}

Repeat the same process for other pages such as ‘projects’ and ‘contact’. Create a file pages/projects.js and pages/contact.js.

// pages/projects.js
import Layout from '../components/Layout';

export default function Projects() {
  return (
    <Layout>
      <h2>Projects</h2>
      <p>Your project listings will go here.</p>
    </Layout>
  );
}
// pages/contact.js
import Layout from '../components/Layout';

export default function Contact() {
  return (
    <Layout>
      <h2>Contact</h2>
      <p>Contact information goes here.</p>
    </Layout>
  );
}

4. Creating Project Cards

Let’s create a reusable component to display our projects. Create a new file components/ProjectCard.js:

import styles from '../styles/ProjectCard.module.css';

function ProjectCard({ title, description, imageUrl, liveLink, githubLink }) {
  return (
    <div className={styles.card}>
      <img src={imageUrl} alt={title} className={styles.image} />
      <h3>{title}</h3>
      <p>{description}</p>
      <div className={styles.links}>
        {liveLink && <a href={liveLink} target="_blank" rel="noopener noreferrer">Live Demo</a>}
        {githubLink && <a href={githubLink} target="_blank" rel="noopener noreferrer">GitHub</a>}
      </div>
    </div>
  );
}

export default ProjectCard;

Create a file styles/ProjectCard.module.css and add some styling:

.card {
  border: 1px solid #ccc;
  padding: 1rem;
  margin-bottom: 1rem;
  border-radius: 8px;
  text-align: center;
  width: 300px;
}

.image {
  width: 100%;
  height: auto;
  margin-bottom: 0.5rem;
  border-radius: 4px;
}

.links {
  margin-top: 1rem;
}

.links a {
  margin: 0 0.5rem;
  text-decoration: none;
  color: #0070f3;
}

Now, go back to pages/projects.js and import and use the ProjectCard component:

import Layout from '../components/Layout';
import ProjectCard from '../components/ProjectCard';

const projectsData = [
  {
    title: 'Project 1',
    description: 'A brief description of project 1.',
    imageUrl: '/project1.png', // Replace with your image path
    liveLink: 'https://example.com/project1', // Replace with your live demo URL
    githubLink: 'https://github.com/yourusername/project1', // Replace with your GitHub URL
  },
  {
    title: 'Project 2',
    description: 'A brief description of project 2.',
    imageUrl: '/project2.png', // Replace with your image path
    liveLink: 'https://example.com/project2', // Replace with your live demo URL
    githubLink: 'https://github.com/yourusername/project2', // Replace with your GitHub URL
  },
  // Add more projects as needed
];

export default function Projects() {
  return (
    <Layout>
      <h2>Projects</h2>
      <div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center' }}>
        {projectsData.map((project, index) => (
          <ProjectCard key={index} {...project} />
        ))}
      </div>
    </Layout>
  );
}

Important: Make sure to replace the placeholder image paths (/project1.png, /project2.png) with actual image files in your public/ directory. You can also customize the project details in the projectsData array.

5. Adding a Contact Form

Let’s add a simple contact form to the pages/contact.js page. This form will allow visitors to send you messages. For simplicity, we’ll only handle the front-end part of the form. Handling the backend (e.g., sending emails) requires server-side code, which is beyond the scope of this basic tutorial.

Modify pages/contact.js:

import Layout from '../components/Layout';
import styles from '../styles/Contact.module.css';

export default function Contact() {
  const handleSubmit = (event) => {
    event.preventDefault();
    // Add your form submission logic here (e.g., sending data to an API)
    alert('Form submitted! (Not actually sending, this is a placeholder)');
    // You would typically send form data to an API here to handle sending emails.
  };

  return (
    <Layout>
      <h2>Contact</h2>
      <form onSubmit={handleSubmit} className={styles.form}>
        <label htmlFor="name">Name:</label>
        <input type="text" id="name" name="name" required />

        <label htmlFor="email">Email:</label>
        <input type="email" id="email" name="email" required />

        <label htmlFor="message">Message:</label>
        <textarea id="message" name="message" rows="4" required />

        <button type="submit">Submit</button>
      </form>
    </Layout>
  );
}

Create a file styles/Contact.module.css and add styling:

.form {
  display: flex;
  flex-direction: column;
  width: 80%;
  max-width: 400px;
  margin: 0 auto;
}

.form label {
  margin-bottom: 0.5rem;
  font-weight: bold;
}

.form input, .form textarea {
  padding: 0.5rem;
  margin-bottom: 1rem;
  border: 1px solid #ccc;
  border-radius: 4px;
}

.form button {
  background-color: #0070f3;
  color: white;
  padding: 0.75rem 1rem;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

6. Adding Image Optimization

Next.js has a built-in Image component that optimizes images automatically, which is crucial for performance. Let’s add it to our project cards.

First, import the Image component from ‘next/image’ in your ProjectCard.js file:

import Image from 'next/image';

Then, replace the standard <img> tag with the Image component:

<Image
  src={imageUrl}
  alt={title}
  width={300} // Adjust these dimensions as needed
  height={200}
  className={styles.image}
  objectFit="cover"
/>

Make sure to adjust the width and height attributes to match your image dimensions and to optimize the layout. The objectFit="cover" property ensures that the image covers the container while maintaining its aspect ratio.

Place your images in the public/ folder.

7. Deploying Your Portfolio

Once you’ve built your portfolio, you’ll want to deploy it so that it’s accessible to the world. Next.js makes deployment easy. Here are a couple of popular options:

  • Vercel: Vercel is the platform created by the same team that built Next.js. It’s the easiest and recommended way to deploy your Next.js application. Simply push your code to a Git repository (like GitHub or GitLab) and connect it to Vercel. Vercel will automatically build and deploy your application.
  • Netlify: Netlify is another excellent platform for deploying static sites and web applications. It also offers continuous deployment features similar to Vercel.

Follow the instructions on the platform of your choice to deploy your application. Generally, this involves connecting your Git repository and configuring the build settings.

Common Mistakes and How to Fix Them

  • Incorrect File Paths: Double-check your file paths, especially when importing components and images. Typos or incorrect relative paths are a common source of errors. Use absolute paths or relative paths correctly.
  • CSS Styling Issues: Ensure your CSS is correctly linked and that you’re using the correct class names. Use your browser’s developer tools to inspect the elements and check for any styling conflicts.
  • Image Optimization Problems: When using the Next.js Image component, make sure the width and height attributes are specified correctly. Also, ensure your images are in the public/ directory.
  • Missing Dependencies: If you encounter errors about missing modules, make sure you’ve installed all necessary dependencies using npm install or yarn install.
  • Incorrect Routing: Verify that your file names in the pages/ directory match the routes you expect. For example, pages/about.js will be accessible at /about.

Key Takeaways

  • Next.js simplifies building modern, SEO-friendly, and performant portfolio websites.
  • Use components to create reusable and maintainable UI elements.
  • Leverage Next.js features like SSR, SSG, and image optimization for optimal performance.
  • Organize your project structure for clarity and scalability.
  • Choose a deployment platform that suits your needs, such as Vercel or Netlify.

FAQ

  1. Can I use a different CSS framework (e.g., Tailwind CSS, Bootstrap)? Yes, you can. Next.js is flexible and allows you to integrate with various CSS frameworks. You’ll need to install the framework and configure it according to its documentation.
  2. How can I add a blog to my portfolio? You can create a blog section by adding a new route (e.g., /blog) and fetching content from a headless CMS (like Contentful or Sanity) or using Markdown files.
  3. How do I handle form submissions on the backend? You’ll need to set up a serverless function or an API endpoint to handle form submissions. This will involve sending emails, storing data in a database, or performing other actions. This tutorial focuses on the front-end.
  4. How can I add animations and transitions? You can use CSS animations or libraries like Framer Motion to add animations and transitions to your portfolio.

Building a portfolio website with Next.js is a rewarding experience. It’s a fantastic way to showcase your skills, learn new technologies, and create a strong online presence. Remember to experiment with different components, styling options, and content to create a portfolio that truly reflects your unique brand.