A developer portfolio is a collection of work samples and projects showcasing your skills and experience. A strong portfolio sets you apart from other candidates when looking for jobs. But not only that: a portfolio can also be a valuable tool for networking, keeping track of your learnings, and establishing yourself as a subject matter expert.

In this tutorial, you will learn how to build a developer portfolio using Next.js and deploy directly from your GitHub repository to Kinsta’s Application Hosting platform, which provides a free .kinsta.app domain to get your work live quickly.

Here’s a live demo of the developer portfolio you’ll be building with Next.js.

You can access this project’s GitHub repository if you’d like to take a closer look, or you can use this portfolio starter project template by selecting Use this template > Create a new repository — this will copy the starter code into a new repository. The starter project contains basic codes like the styles, a Font Awesome CDN link, images, and basic structure.

Requirements/Prerequisites

This is a “follow-along” type of tutorial. It will be easier for you to code along if you have:

Why Next.js?

Next.js is a React-based open-source JavaScript library framework that can be used for a wide range of web development projects because it simplifies building server-side rendered and static applications. It streamlines the process by leveraging the best features of React and optimizing rendering performance for improved user experience. Some of the most common use cases for Next.js include:

  • Building static websites: Next.js can build static websites that are fast, easy to deploy, and require minimal maintenance, like the developer portfolio website we’ll be building throughout this tutorial.
  • Building dynamic websites: Next.js allows you to create dynamic websites that can change content based on user interactions or server-side data fetching.
  • Building ecommerce websites: Next.js is well-suited for building ecommerce websites that require server-side rendering for improved SEO and performance.
  • Building progressive web applications (PWAs): Next.js supports the creation of PWAs, which are web applications that function like native apps and can be installed on a user’s device.

How To Set Up Your Next.js Development Environment

To set up a development environment for Next.js, first install Node.js on your computer, because you will use the npx command to run npm packages without having to install them globally on your system. Once that is taken care of, you can now create a Next.js project by running the following command:

npx create-next-app@latest my-portfolio

A prompt will appear asking you to confirm some additional dependencies. Then you can run npm run dev to make your app available at localhost:3000.

Code showing a new next.js project.
Creating a new Next.js project.

When creating a Next.js project using the npx command, it automatically scaffolds a folder structure with the following main directories:

  1. pages: This folder contains the application’s pages, which are automatically routed based on their file name. For example, pages/index.js would be the home page, while pages/about.js would be the about page.
  2. public: This folder contains static files that can be served directly, such as images, fonts, and other assets.
  3. components: This folder is optional and contains reusable UI components that can be used across the application.
  4. styles: This folder is also optional and contains global styles that can be applied across the application.

Other directories and files may also be generated depending on the specific configuration and features, but these are the core directories of a basic Next.js project.

For this tutorial, everything that we build will appear on the index page (our one-page website), and you will include components for various sections like the hero, about, projects, and others.

How To Build a Responsive Developer Portfolio Using Next.js

A portfolio typically consists of components like these:

  • Navbar component
  • Hero component
  • About component
  • Skills component
  • Projects component
  • Contact component
  • Footer component

The Navbar and Footer components are expected to appear on all pages if the portfolio has more than one page. This can be achieved in Next.js by defining a layout.

Defining Layouts in Next.js

In Next.js, a layout is a way to define a consistent structure for the components that appear on every page of a website. The layout usually includes elements such as a header, navigation menu, and footer displayed across all site pages.

Start by creating a components folder in the src (source) directory of your Next.js project. Next, create the Navbar and Footer components that will be used within the Layout component.

Here’s the Navbar component in Navbar.jsx:

// components/Navbar.jsx

import Link from "next/link";

const Navbar = () => {
  return (
    <div className="nav-container">
      <div className="logo">
        <Link href="/">
          Joe's Portfolio
        </Link>
      </div>
      <a href="" className="cta-btn">Resume</a>
    </div>
  )
}

export default Navbar;

Here’s the Footer component in Footer.jsx:

// components/Footer.jsx

const Footer = () => {
  return (
    <>
      <hr/>
      <div className="footer-container">
        <p>
          © {new Date().getFullYear()} Joel's Portfolio
        </p>
        <div className="social_icons">
          <a
            href="https://twitter.com/olawanle_joel"
            aria-label="Twitter"
            target="_blank"
            rel="noopener noreferrer"
          >
            <i className="fa-brands fa-twitter"></i>
          </a>
          <a
            href="https://github.com/olawanlejoel"
            aria-label="GitHub"
            target="_blank"
            rel="noopener noreferrer"
          >
            <i className="fa-brands fa-github"></i>
          </a>
          <a
            href="https://www.linkedin.com/in/olawanlejoel/"
            aria-label="LinkedIn"
            target="_blank"
            rel="noopener noreferrer"
          >
            <i className="fa-brands fa-linkedin"></i>
          </a>
        </div>
      </div>
    </>
  )
}

export default Footer;

Note: For the Font Awesome icons to work, you must either install Font Awesome into your project or use its CDN. You can add the CDN link to your _document.js file like this:

// pages/_document.js

import { Html, Head, Main, NextScript } from 'next/document';

export default function Document() {
  return (
    <Html lang="en">
      <Head>
        <meta charSet="utf-8" />
        <link
          rel="stylesheet"
          href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css"
          integrity="sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w=="
          crossorigin="anonymous"
          referrerpolicy="no-referrer"
        />
      </Head>
      <body>
      <Main />
      <NextScript />
      </body>
    </Html>
  );
}

Note: If you link in a different version of Font Awesome via the CDN, you will need to swap in above the appropriate integrity hash for that release.

After creating all the necessary components for your layout, you can create the Layout component itself and add this component to your pages by wrapping your page content within it.

The Layout component will accept a <code>children</code> prop, allowing you to access the content of your Next.js pages.

// components/Layout.jsx

import Navbar from './navbar';
import Footer from './footer';

const Layout = ({ children }) => {
  return (
    <>
      <Navbar />
      <main>{children}</main>
      <Footer />
    </>
  )
}

export default Layout;

At this point, you have successfully created the Layout component which holds the Navbar and Footer alongside the children props positioned properly. You can now add the Layout component to your pages by wrapping the page content in it. This will be done in the _app.js file.

// pages/_app.js

import '@/styles/globals.css';
import Layout from '../components/layout';

export default function App({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  );
}

You have now succeeded in creating a layout for your developer portfolio. For this portfolio, we focus more on Next.js and how to deploy your website to Kinsta. So you can copy the styles in the styles/globals.css file into your own project. If you launch your portfolio website in dev mode, you should now see your app’s layout.

Image of a layout component
Layout component

Now it’s time to give your portfolio website the appropriate content.

Building Portfolio Components

You can now create individual components for each section of your developer’s portfolio. All these components will be imported into the index page of your Next.js project, so they can display when you launch your project with npm run dev.

The Hero Component

The Hero component is the first section below the Navbar, whose main purpose is to capture the user’s attention and give them a sense of what the website or application is about.

// components/Hero.jsx

import Image from "next/image";

const Hero = () => {
  return (
    <div className="hero-container">
      <Image src='/images/profile.jpeg' className="profile-img" width={300} height={300} alt="Joe's personal headshot" />
      <div className="hero-text">
        <h1>Hey, I'm Joe 👋</h1>
        <p>
          I'm a software developer based in Lagos, Nigeria. I specialize in building (and occasionally designing)
          exceptional websites, applications, and everything in between.
        </p>
        <div className="social-icons">
          <a
            href="https://twitter.com/olawanle_joel"
            aria-label="Twitter"
            target="_blank"
            rel="noopener noreferrer"
          >
            <i className="fa-brands fa-twitter"></i>
          </a>
          <a
            href="https://github.com/olawanlejoel"
            aria-label="GitHub"
            target="_blank"
            rel="noopener noreferrer"
          >
            <i className="fa-brands fa-github"></i>
          </a>
          <a
            href="https://www.linkedin.com/in/olawanlejoel/"
            aria-label="LinkedIn"
            target="_blank"
            rel="noopener noreferrer"
          >
            <i className="fa-brands fa-linkedin"></i>
          </a>
        </div>
      </div>
    </div>
  )
}

export default Hero;

In the code above, you will notice that the Next.js Image component is used instead of the HTML img tag to add the image because it enables automatic image optimization, resizing, and lots more.

In the About component, you will also notice that a simple paragraph to say little about the developer was added alongside some social icons from Font Awesome to add social links.

This is what the Hero component should look like:

Next.js hero component for portfolio website
Hero Component

You can add more content to the Hero component, tweak the styles in the styles/globals.css file or even recreate this section in your own way.

The About Component

The About component is meant to tell readers or people that visit your portfolio more information about you in as many paragraphs as you want. If you wish to tell more about yourself, you can create a dedicated “About Me” page and add a button on this section to read more about yourself.

// components/About.jsx

import Image from "next/image";

const About = () => {
  return (
    <div className="about-container">
      <h2>About Me</h2>
      <div className="flex-about">
        <div className="about-text">
          <p>
            As a developer, I have always been passionate about creating elegant and effective solutions to
            complex problems. I have a strong foundation in software development, with a focus on web
            technologies such as HTML, CSS, and JavaScript. I enjoy working on both the front-end and
            back-end of applications, and I am always looking for ways to optimize performance, improve user
            experience, and ensure the highest level of code quality.
          </p>
          <p>Throughout my career, I have worked on a wide range of projects, from simple static websites to
            complex enterprise-level applications. I am experienced in working with a variety of development
            tools and frameworks, including React, Angular, Vue.js, Node.js, and Laravel. I am always eager
            to learn and explore new technologies, and I am constantly seeking out opportunities to improve
            my skills and knowledge.</p>
        </div>
        <div className="about-img">
          <Image src='/images/about.jpeg' className="profile-img" width={300} height={500}/>
        </div>
      </div>
    </div>
  )
}

export default About;

The code above contains two paragraphs of text about the developer and an image of the developer. This is what the About section is expected to look like:

The About component on a Next.js portfolio site
About component

You can always tweak the styles to add more images and lots more.

The Skills Component

The skills component is meant to show some of the developer’s most used technologies or technologies the developer has used in the past.

Skills component
Skills component

You can make this easier to maintain by creating an array in an external file and then import into the skills component, so you can loop through instead of duplicating similar code.

// components/Skills.jsx

const Skills = () => {
  return (
    <div className="skills-container">
      <h2>Skills</h2>
      <div className="grid-skills">
        <div className="skill-card html">
          <i className="fa-brands fa-html5 html-icon"></i>
          <p>HTML</p>
        </div>
        <div className="skill-card css">
          <i className="fa-brands fa-css3-alt css-icon"></i>
          <p>CSS</p>
        </div>
        <div className="skill-card js">
          <i className="fa-brands fa-js-square js-icon"></i>
          <p>JavaScript</p>
        </div>
        <div className="skill-card react">
          <i className="fa-brands fa-react react-icon"></i>
          <p>React</p>
        </div>
        <div className="skill-card node">
          <i className="fa-brands fa-node-js node-icon"></i>
          <p>Node</p>
        </div>
        <div className="skill-card python">
          <i className="fa-brands fa-python python-icon"></i>
          <p>Python</p>
        </div>
      </div>
    </div>
  )
}

export default Skills;

In the code above, a card is created for each skill, and each card will hold the technology icon from font-awesome and the technology name. You can also add more styles and tweak the code to make it more attractive and unique.

The Projects Component

The project component is one of the important sections of a developer’s portfolio. Projects provide tangible evidence of a developer’s skills and abilities and showcase their ability to apply their knowledge to real-world problems.

Each project will include a brief description of the project, a link to its source code (we’re using GitHub links here), and any other details you wish to add.

Next.js projects component for portfolio website
Projects component

You can create an array to hold each project’s details and then import it into your component to avoid hard-coding them.

Let’s create a data.js file to store the array of project data. You can store this file in the component folder or the pages/api folder. For this demo, I will store it in the components folder. This array will hold an object for each project, and the object will hold the project name, description, and GitHub link.

// components/data.js

export const projectData = [
  {
    id: 1,
    title: 'Todo List App',
    description:
      'A simple Todo List App built with JavaScript. All datas are stored in localstorage. It helps users check list out their plans and tick as they do them.',
    gitHubLink: 'https://github.com/olawanlejoel/Todo-List-App',
  },
  {
    id: 2,
    title: 'Books Library App',
    description:
      'A simple Book Library App built with JavaScript. It helps readers have a good list of books they are either currently reading or have finished reading.',
    gitHubLink: 'https://github.com/olawanlejoel/Book-Library',
  },
  {
    id: 3,
    title: 'Quotes Generator',
    description:
      'Helps you generate quotes from about 1600 quotes written by different authors . Quotes are automatically copied to your clipboards.',
    gitHubLink: 'https://github.com/olawanlejoel/random-quote-generator',
  },
  {
    id: 4,
    title: 'Password Generator',
    description:
      'Helps you generates random passwords, you can select what you want your password to entail and also you can copy generated password to clipboard.',
    gitHubLink: 'https://github.com/olawanlejoel/Password-Generator',
  },
  {
    id: 5,
    title: 'Twitter UI Clone',
    description:
      'Simple Twitter UI clone built with TailwindCSS and Vue Js. This covers only the homepage of Twitter UI. This is cool to get started with TailwindCSS as it helps understand basic concepts.',
    gitHubLink: 'https://github.com/olawanlejoel/TwitterUI-clone',
  },
];

You can now create a project component to utilize this data by looping through easily. You can use any JavaScript iteration method, but for this tutorial, you can use the JavaScript map() array method to loop through the data array after importing it into the Projects component.

// components/Projects.jsx

import { projectData } from './data.js';

const Projects = () => {
  return (
    <div className="projects-container">
      <h2>Projects</h2>
      <div className="projects-grid">
        {projectData && projectData.map((project) => (
          <div className="project-card" key={project.id}>
            <div className="project-header">
              <i className="fa-regular fa-folder-open folder-icon"></i>
              <div className="small-icons">
                <a href={project.gitHubLink}><i className="fa-brands fa-github"></i></a>
              </div>
            </div>
            <h3>{project.title}</h3>
            <p>{project.description}</p>
          </div>
        ))
        }
      </div>
    </div>
  )
}

export default Projects;

In the code above, you have successfully avoided repetition by looping through the array to output all projects into the Projects component making it easy to maintain and add more projects.

The Contact Component

One reason to create a developer’s portfolio is so potential clients can reach out to you. One way would be for people to send you an email, which is what we’ll facilitate in this Contact component.

// components/Contact.jsx

const Contact = () => {
  return (
    <div className="contact-container">
      <h2>Get In Touch</h2>
      <p>If you want us to work together, have any questions or want me to speak at your event, my inbox is always open. Whether I just want to say hi, I'll try my best to get back to you! Cheers!</p>
      <a href="mailto:[email protected]" className='cta-btn'>Say Hello</a>
    </div>
  )
}

export default Contact;

Place your email address in the a tag so that the button will launch an email application with a message addressed to you.

Contact component for the Next.js portfolio website
Contact component

You have now successfully created all the components for your portfolio application. The next step would be to add them to your index page. Navigate to pages/index.js file — which is created by default — and replace its code with the following.

// pages/index.js

import Hero from '@/components/Hero';
import About from '@/components/About';
import Skills from '@/components/Skills';
import Projects from '@/components/Projects';
import Contact from '@/components/Contact';

import Head from 'next/head';

const Home = () => {
  return (
    <>
      <Head>
        <title>Joel's Portfolio</title>
        <meta name="description" content="Joel's Portfolio" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <div>
        <Hero />
        <About />
        <Skills />
        <Projects />
        <Contact />
      </div>
    </>
  );
};

export default Home;

When you now run your application, you will notice a full portfolio website has been created. Finally, before deploying your application, let’s install a dependency. One advantage of using Next.js is the many features it brings to the table, such as file-based routing, image optimization, and lots more.

Image optimization is handled with the Next.js Image component. Before deploying an application into production that uses the Next.js Image component, it is strongly recommended that you install sharp. You can do this by navigating to your terminal, ensuring you are in your project’s directory, and then running the following command:

npm i sharp

You can now deploy your application, and the images will work properly with the full optimization that Next.js provides.

How To Deploy Next.js Application to Kinsta

Once you’re happy with your portfolio showcasing your best development work and key information, you will likely want to share it with others, right? Let’s see how to do that using GitHub and Kinsta’s Application Hosting platform.

Push Your Code to GitHub

There are various ways to push codes to GitHub, but let’s use Git for this tutorial. Git is widely used in software development because it provides a reliable and efficient way to manage code changes, collaborate on projects, and maintain version history.

You can upload your code to GitHub using the following steps:

First, create a new repository (just like a local folder to store your code). You can do this by logging in to your GitHub account, clicking on the + button in the top right corner of the screen and selecting New repository from the dropdown menu as seen in the image below.

Creating a new GitHub repository
Create a new GitHub repository.

The next step would be to give your repository a name, add a description (optional), and select whether you want your repository to be public or private. Then click Create repository. You can now push your code to the new GitHub repository.

All that is needed to push your code with Git is the repository URL, which you can find on the repository’s main page, under the Clone or download button, or in the steps that appear after creating a repository.

Accessing your GitHub repository URL
Access your GitHub repository URL

You can prepare to push your code by opening your terminal or command prompt and navigating to the directory that contains your project. Use the following command to initialize a local Git repository:

git init

Now add your code to the local Git repository using the following command:

git add .

The above command adds all files in the current directory and its subdirectories to the new Git repository. You can now commit your changes using the following command:

git commit -m "my first commit"

Note: You can replace “my first commit” with your own brief message describing the changes you made.

Finally, push your code to GitHub using the following commands:


git remote add origin [repository URL]
git push -u origin master

Note: Ensure you replace “[repository URL]” with the URL of your own GitHub repository.

Once you have completed these steps, your code will be pushed to GitHub and accessible through your repository’s URL. You can now deploy your repository to Kinsta.

Deploy Your Portfolio to Kinsta

Deployment to Kinsta happens in just minutes. Start at the My Kinsta dashboard to log in or create your account.

Next, you will authorize Kinsta on GitHub in these quick steps:

  1. Click Applications on the left sidebar
  2. Click Add service
  3. From the dropdown menu, click Application because you want to deploy a Next.js application to Kinsta.
Create an application project in MyKinsta
Create an application project in MyKinsta

A modal will appear through which you can select the repository you wish to deploy.

If you have multiple branches in your repository, you can select the one you wish to deploy. You can also assign a name to this application. Make sure to select a data center location among the 25 available, and then Kinsta will automatically detect a start command.

Automatically detect start command
Automatically detect start command

At this point, your application will start deploying. Within a few minutes, a link will be provided to access the deployed version of your application. In this case, it is: https://kinsta-developer-portfolio-ir8w8.kinsta.app/

Deployment link on Kinsta
Deployment link on Kinsta

Note: Automatic deployment was enabled, so Kinsta automatically re-deploys your application whenever you make changes to your codebase and push it to GitHub.


Summary

There are several reasons why developers should consider using Next.js for their web projects. First, it provides optimized performance out-of-the-box, with features such as pre-fetching and code splitting that help reduce page load times. Second, it provides a familiar development experience for React developers, supporting popular tools such as styled components and the latest React features.

Kinsta provides support for various deployment options for Next.js, including traditional server-based hosting and modern serverless platforms. This allows developers to choose the deployment option that best suits their needs while benefiting from the framework’s performance optimizations and other benefits.

In this tutorial, you have learned step-by-step how to build a responsive portfolio site using Next.js and then deploy it to Kinsta.

You can try Kinsta’s Application Hosting for free, and if you like it, opt for our Hobby Tier plan starting at $7/month.

Now it’s your turn to challenge yourself: add more features to your newly developed portfolio website! Here are a few ideas to get your creative juice flowing: add more pages with detailed information, integrate a blog with MDX, implement some animation. Share your projects and experience in the comments below!

Joel Olawanle Kinsta

Joel is a Frontend developer working at Kinsta as a Technical Editor. He is a passionate teacher with love for open source and has written over 200 technical articles majorly around JavaScript and it's frameworks.