Bits Beyond

How to create a next.js blog application with mdx and static generation

Cover Image for How to create a next.js blog application with mdx and static generation

Prerequisites

To create a next.js application you need to have Node.js installed on your machine. You can download Node.js from the official website here.

Creating a next.js blog application

In this post we will create a next.js blog application with markdown support and static generation at build time. First it is important to understand the differences between server-side rendering (SSR) and static generation (SG).

  • Server-side rendering (SSR): The html for a page is generated on the server on each request. This means that the content is always up-to-date but the page load time is slower.
  • Static generation (SG): The html for a page is generated at build time. This means that the content is static and the page load time is faster.

These two types can also be mixed, meaning your blog can have some pages that are statically generated and some that are server-side rendered.

Having that in mind, let's create our next.js blog application by running the following command:

npx create-next-app my-next-js-blog

We will select the default configuration.

Now we can navigate to the newly created directory.

cd my-next-js-blog

Dependencies

To add markdown support to our next.js blog application we need to install the following dependencies:

npm install gray-matter remark remark-html next-mdx-remote

Our first blog post

Create a _posts directory in the root of your project and add a markdown file my-first-blog-post.mdx with the following content:

_posts/my-first-blog-post.md
---
title: "My First Blog Post"
date: "2024-08-08"
---
 
Welcome to my first blog article!

Between the --- is the front matter, which is metadata about the post. In this case we have the title and the date of the post.

Reading the blog posts from the file system

For now we will read the blog posts from the file system. Create a getPosts function in a new file src/lib/posts.ts:

src/lib/posts.ts
import fs from "fs";
import matter from "gray-matter";
import { join } from "path";
 
const postsDirectory = join(process.cwd(), "_posts");
 
export interface Post {
  title: string;
  date: string;
  content: string;
  slug: string;
}
 
export function getPostSlugs() {
  return fs.readdirSync(postsDirectory);
}
 
export function getPostBySlug(slug: string) {
  const realSlug = slug.replace(/\.mdx$/, "");
  const fullPath = join(postsDirectory, `${realSlug}.mdx`);
  const fileContents = fs.readFileSync(fullPath, "utf8");
  const { data, content } = matter(fileContents);
 
  return { ...data, slug: realSlug, content } as Post;
}
 
export function getAllPostsSortedByDate(): Post[] {
  const slugs = getPostSlugs();
  const posts = slugs
    .map((slug) => getPostBySlug(slug))
    .sort((post1, post2) => (post1.date > post2.date ? -1 : 1));
  return posts;
}

Displaying the blog posts

Now we can display the blog posts on the home page. Create a new file src/app/page.tsx with the following content:

src/app/page.tsx
import { getAllPostsSortedByDate } from "@/lib/posts";
import Link from "next/link";
 
export default function Home() {
  const posts = getAllPostsSortedByDate();
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.slug}>
          <Link href={`blog/${post.slug}`}>{post.title}</Link>
          <p>{post.date}</p>
        </li>
      ))}
    </ul>
  );
}

The function getAllPostsSortedByDate will get all posts from the file system and display the title and date of each post aswell as offering a link to go to the blog article. Speaking of which we should create the blog page now.

Displaying a single blog post

Create a new file src/pages/blog/[slug].tsx with the following content:

src/pages/blog/[slug].tsx
import { MDXRemote } from "next-mdx-remote/rsc";
import { getAllPostsSortedByDate, getPostBySlug } from "@/lib/posts";
 
export async function generateStaticParams() {
  const posts = getAllPostsSortedByDate();
 
  return posts.map((post) => ({
    slug: post.slug,
  }));
}
 
const BlogPage = (props: { params: { slug: string } }) => {
  const post = getPostBySlug(props.params.slug);
  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.date}</p>
      <MDXRemote
        source={post.content}
        options={{
          mdxOptions: {
            remarkPlugins: [],
            rehypePlugins: [],
          },
        }}
        components={{}}
      />
    </div>
  );
};
 
export default BlogPage;

With remarkPlugins and rehypePlugins you can add plugins to the markdown parser. Some popular plugins are remark-gfm for GitHub flavored markdown and rehype-pretty-code for syntax highlighting.

Now when you build your next.js application with npm run build you will see that the blog posts are statically generated at build time.

Route (app)                              Size     First Load JS
 /                                    6.95 kB          94 kB
 /_not-found                          871 B          87.9 kB
 /blog/[slug]                         137 B          87.2 kB
 /blog/my-first-blog-post
+ First Load JS shared by all            87 kB
 chunks/23-b75664ace61c0abb.js        31.5 kB
 chunks/fd9d1056-2821b0f0cabcd8bd.js  53.6 kB
 other shared chunks (total)          1.86 kB
 
 
  (Static)  prerendered as static content
  (SSG)     prerendered as static HTML (uses getStaticProps)

Great! We built a next.js blog application with markdown support and static generation at build time. 🚀
In the next read we will explore how to deploy our next.js blog application on docker hub. Stay tuned!

Read Next

Cover Image for How to create a next.js application with create-next-app

How to create a next.js application with create-next-app

In this post we will explore how to create a next.js application using create next app.

Cover Image for How to deploy a next.js application on docker hub

How to deploy a next.js application on docker hub

Explore how to deploy a next.js application on docker hub.

Cover Image for How to work with a next.js application

How to work with a next.js application

In this post we will explore how to with a next.js application to add pages, support routing and dynamic routes.