Skip to main content

How to build a portfolio with Next

Nabil-Y / 2022-09-02
7 min read

Developers thinking in front of a screen

Where to start ?

Building a personal project from scratch is hard if you don't know where to start.

I'll try to help and tell you my thought process behind this website's creation.

First, defining what you want to do. For this portfolio, there were two main needs:

  1. Showcase projects
  2. Write blog articles

Each one has many sub-topics. How to show the projects? How to store the data ? What framework to use ? Can I add a like button ? Add a view count ? Should I use a CMS ? A self-hosted database ? Markdown ?

At first I was getting lost trying to figure out what to do or what to use, what might be useful for the future. Then I stumbled upon the YAGNI principle.

YAGNI

Y'Aint Gonna Need It

Short and clear. Us developpers tend to implement features or write code that we might need later.

And more often than not, it ends up being unused. So much time is being wasted.

That's where the YAGNI principle comes in. Only write code that you need now. Might seem counter-intuitive but it's very useful.

It saved me so much time to make a first version of this portfolio.

Remember this: Code a feature when you need it, not before.

The tech

Next comes the time to think about what technology is best suited for your project.

Even before starting I knew I'd use TypeScript because it makes your code self-documented and way more reliable than JavaScript.

Also TailwindCSS just because I'm a big fan. It makes the styling part a pleasure with component frameworks like React.

It may or may not be the main topic of a future blog post.

./tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  darkMode: "class",
  theme: {
    extend: {
      fontFamily: {
        sora: ["'Sora'", "sans-serif"],
      },
      colors: {
        // colors
      },
      animation: {
        appear: "appearance 0.2s linear",
      },
      keyframes: {
        appearance: {
          "0%": { transform: "translateY(16px)", opacity: "0" },
          "100%": { transform: "translateY(0)", opacity: "1" },
        },
      },
    },
  },
  plugins: [require("@tailwindcss/typography")],
};

For a blog, what comes to mind first is using a CMS.

The idea of using a dedicated headless CMS like Sanity or Strapi was appealing but I wanted to build something simple by myself...

Then I chose Next.js for its Static Site Generation (SSG) feature and the many optimisations it comes with.

Also the blog will not be hosted on a blogging platform so I need SEO to expose my articles to search engines.

Next.js has built in SEO tools so that's a big plus.

The blog

Static Site Generation is one of the most optimized way to create blogs because the data doesn't change much.

As a result, each page has its own HTML file created and filled with data, ready to be served to your browser. It's blazingly fast.

And with the proper setup, your GitHub repo can act like a free and easy to use headless CMS.

To do so, all my blog posts are written in the /data/posts folder and their content is scraped thanks to Node's fs module and gray-matter.

./libs/posts.ts
import fs from "fs";
import path from "path";
import matter from "gray-matter";
import readingTime from "reading-time";

const postsDirectory = path.join(process.cwd(), "data", "posts");

export const getAllPostsSorted = () => {/* code */ };

export const getAllPostsPaths = () => { /* code */ };

export const getPostData = async (slug: string) => {
  const filePath = path.join(postsDirectory, `${slug}.mdx`);
  const fileContent = fs.readFileSync(filePath, "utf8");
  const matterResult = matter(fileContent);
  const readTime = readingTime(matterResult.content).text;

  return {
    meta: matterResult.data,
    mdx: matterResult.content,
    readTime,
  };
};

The gray-matter module parses text files and separates meta data ( like the title, description, thumbnail of the page) and the main content of the file.

These functions can then be reused in all pages to return the posts data stored in /data/posts.

Next.js will use this dynamic page /pages/blog/[slug].tsx as a template and generate a static page for each file in the posts folder.

For the articles, I chose MDX (which stands for Markdown + JSX) instead of simple markdown for three main reasons.

  • Be able to use the built-in Next.js Image component to optimize automatically article images.

  • I was curious about MDX and wanted to learn by using it in a project. (The quickest way to understand new technologies)

  • I could import custom React components and make future articles more interactive (not really YAGNI but still, I learned a lot so it's worth it).

./data/posts/how-to-build-a-portfolio.mdx
---
  title: "How to build a portfolio-with-next"
  slug: "how-to-build-a-portfolio-with-next"
  date: "2022-09-02"
  description: "The thought process and technologies ..."
  image: "/static/blog/why-this-blog/blogging.png"
---

## Where to start ?

Building a personal project from scratch is hard ...

You might notice the about.mdx file with the blog posts. It is the about section that is linked in the top navbar.

Using MDX would allow me to make quicker edits and still be able to use React components if needed.

So I wanted MDX but without setting it up a second time or writing a new function to scrape the content of the about page.

To work around this, I simply reused the blog post logic and hid some of the blog specific html blocks like reading time or date.

./pages/blog/[slug].tsx
// If title is "About me" then the following html will not be used.
// This method is called short-circuiting

{title !== "About me" && (
  <div className="flex justify-between text-greyDark dark:text-grey">
    <div>Nabil-Y / {date}</div> <div>{readingTime}</div>
  </div>
)}

The portfolio

For the projects part, I wanted something very simple.

Really had no desire to spend lots of time finding and implementing a storage solution.

There were many options like a database, CMS or reusing the original scraping method of fs module.

Instead I opted for a quick and easy one. Yes...

A simple array. Easy and quick but this method can have limitations in readability when I have many, many projects.

Still, It works great for now so there is no need to overcomplicate things.

./types/types.ts
// Array of Project objects

interface Project {
  repoLink: string;
  demoLink: string;
  title: string;
  tags: string[];
  description?: string;
  imgLink?: string;
}

The description and imgLink keys are optional so I can reuse the component and omit them in the Home Page. Thinking about reusability is key in component frameworks.

How to start a new project

In summary, here are the steps to quickstart your new project:

  1. Write the main use cases / user stories
  2. Choose the most adequate langages/frameworks/packages for said use cases
  3. Write a to do list of the minimum number of tasks to complete the project. Keep YAGNI in mind.
  4. Prepare some coffee (or your prefered drink) and start coding!

Template

Word cloud with "Open source" theme

As a contribution to open source, I made a template so that you can build a blog / portfolio like this one in just a few minutes.

Here are the links:

To deploy your portfolio, I advise you use Vercel, it's free and easy to use. Also Next.js is made by Vercel.

To get started, follow the instructions in the repository's readme.

Please star the project if you found it helpful!

Follow me or send a message on