Building a Blog with Next.js and Markdown Link to heading

Creating a blog can be a fantastic way to share your knowledge, grow your personal brand, or simply express your ideas. In this tutorial, we’ll walk through the process of building a blog using Next.js and Markdown. We’ll focus on making it educational and easy to follow, ensuring you understand each step.

Why Choose Next.js and Markdown? Link to heading

Next.js is a powerful React framework that offers server-side rendering, static site generation, and many other features that make it ideal for building fast, SEO-friendly websites. Markdown, on the other hand, is a lightweight markup language that allows you to write content in a simple, readable format.

Combining Next.js with Markdown enables you to create a blog that is both performant and easy to manage. Let’s get started!

Setting Up Your Next.js Project Link to heading

First, you’ll need to set up a new Next.js project. If you haven’t already installed Node.js and npm, you’ll need to do that first. Once you have Node.js and npm installed, you can create a new Next.js project using the following command:

npx create-next-app my-nextjs-blog
cd my-nextjs-blog

This command will create a new directory called my-nextjs-blog and set up a basic Next.js project inside it.

Adding Markdown Support Link to heading

Next.js doesn’t support Markdown out of the box, so we’ll need to install a few additional packages:

npm install gray-matter remark remark-html
  • gray-matter: Parses front matter from Markdown files.
  • remark: A Markdown processor powered by plugins.
  • remark-html: Converts Markdown to HTML.

Creating a Markdown Blog Post Link to heading

Create a new directory called posts in the root of your project. Inside this directory, create a new Markdown file called my-first-post.md:

---
title: "My First Blog Post"
date = "2024-06-14T05:03:50Z"
---

# My First Blog Post

Welcome to my first blog post! This is written in Markdown.

Loading Markdown Files in Next.js Link to heading

We’ll create a function to load Markdown files and convert them to HTML. Create a new file called lib/posts.js:

import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
import { remark } from 'remark';
import html from 'remark-html';

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

export function getSortedPostsData() {
  const fileNames = fs.readdirSync(postsDirectory);
  const allPostsData = fileNames.map((fileName) => {
    const id = fileName.replace(/\.md$/, '');
    const fullPath = path.join(postsDirectory, fileName);
    const fileContents = fs.readFileSync(fullPath, 'utf8');
    const matterResult = matter(fileContents);

    return {
      id,
      ...matterResult.data,
    };
  });

  return allPostsData.sort((a, b) => {
    if (a.date < b.date) {
      return 1;
    } else {
      return -1;
    }
  });
}

export async function getPostData(id) {
  const fullPath = path.join(postsDirectory, `${id}.md`);
  const fileContents = fs.readFileSync(fullPath, 'utf8');
  const matterResult = matter(fileContents);
  const processedContent = await remark().use(html).process(matterResult.content);
  const contentHtml = processedContent.toString();

  return {
    id,
    contentHtml,
    ...matterResult.data,
  };
}

Creating Blog Pages Link to heading

Next, we need to create the pages to display our blog posts. Start by modifying pages/index.js to list all blog posts:

import { getSortedPostsData } from '../lib/posts';
import Link from 'next/link';

export async function getStaticProps() {
  const allPostsData = getSortedPostsData();
  return {
    props: {
      allPostsData,
    },
  };
}

export default function Home({ allPostsData }) {
  return (
    <div>
      <h1>My Blog</h1>
      <ul>
        {allPostsData.map(({ id, date, title }) => (
          <li key={id}>
            <Link href={`/posts/${id}`}>
              <a>{title}</a>
            </Link>
            <br />
            <small>{date}</small>
          </li>
        ))}
      </ul>
    </div>
  );
}

Then, create a new file called pages/posts/[id].js to display individual blog posts:

import { getAllPostIds, getPostData } from '../../lib/posts';

export async function getStaticProps({ params }) {
  const postData = await getPostData(params.id);
  return {
    props: {
      postData,
    },
  };
}

export async function getStaticPaths() {
  const paths = getAllPostIds();
  return {
    paths,
    fallback: false,
  };
}

export default function Post({ postData }) {
  return (
    <div>
      <h1>{postData.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} />
    </div>
  );
}

Generating Paths for Blog Posts Link to heading

To generate the paths for our blog posts, we need to add the following function to lib/posts.js:

export function getAllPostIds() {
  const fileNames = fs.readdirSync(postsDirectory);
  return fileNames.map((fileName) => {
    return {
      params: {
        id: fileName.replace(/\.md$/, ''),
      },
    };
  });
}

Styling the Blog Link to heading

You can style your blog using CSS. Create a file called styles/global.css and add some basic styles:

body {
  font-family: Arial, sans-serif;
  line-height: 1.6;
}

h1 {
  color: #333;
}

ul {
  list-style: none;
  padding: 0;
}

li {
  margin: 0.5rem 0;
}

small {
  color: #666;
}

Then, import this CSS file in pages/_app.js:

import '../styles/global.css';

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

Deploying Your Blog Link to heading

Once you’re happy with your blog, you can deploy it using Vercel, the company behind Next.js. Simply run the following command:

vercel

Follow the prompts to deploy your blog.

Conclusion Link to heading

Congratulations! You’ve built a fully functional blog using Next.js and Markdown. This setup allows you to write and manage your blog posts easily while benefiting from the performance and SEO advantages of Next.js.

For further reading, check out the Next.js documentation and the Markdown Guide.

Next.js Logo

Markdown Logo