How to create a simple static blog with Next.js and markdown
September 8, 2024
Although there are more immediate solutions for creating a blog, I have always been fascinated by static site generators, especially for this type of application: they are easy to maintain and generally perform well. Of course, the world is full of such tools, and I have no reason to prefer Next.js to Hugo, Astro, etc. And probably Next.js is slightly overkill for just a static blog.
But then, why did I choose Next.js to create my site? Well, mainly because I consider the knowledge of Next.js a must-have skill for a web developer in 2024, and I wanted to work with the new App Router. Second, I needed React for features that I plan to add in the future. And, despite my reluctance to use frameworks as a first or only solution, I consider Next.js a nice and comfortable environment to work with React.
So, what features are we going to discuss? As per the title, the blog will be static using Next.js (App Router). Thanks to MDX, posts can be written in Markdown. Finally, we will use Rehype Pretty Code for syntax highlighting.
Let's start.
Setting up the Next.js project
In this guide, I will use TypeScript and Next.js 14 with the new App Router. So be sure to select both at installation time with the command:
Integrating MDX for markdown content
To use markdown to write our posts and simultaneously use JSX in the content, we need to install MDX. To install the package and its dependencies, run the command:
And update the next.config.mjs file to allow you to use MDX and have the .mdx files act as pages and routes in the application:
File: next.config.mjs
Important info:
Since we will have to use Rehype which is ESM only, you will have to use
next.config.mjs as the file for configuration.
As a final step, we need to add an mdx-components.tsx file in the root of the project. This file can be used to extend the default components. For example, you could extend the H1 tag and add Tailwind classes to style it. And so on.
File: mdx-components.tsx
Important info:
Without this file
@next/mdx cannot work.
At this point, you will be able to use page.mdx files inside the /app folder to write content in both Markdown and JSX.
Setting up routing for articles and categories
The idea is to use dynamic routing to organize articles and categories. So, /app/blog/(posts)/hello-world/page.mdx for posts and /app/category/[category]/page.tsx for categories.
Posts
First, we take advantage of the metadata object to add some information in posts:
File: /app/blog/(posts)/hello-world/page.mdx
Let's add some content to verify that the markdown is properly converted to HTML:
File: /app/blog/(posts)/hello-world/page.mdx
At this point, all we have to do is write the function to retrieve all posts. We know that all posts are in the /app/blog/ folder, so we need to read the names of all the folders in the /app/blog/ folder to get a list of all the post slugs. Thanks to this list, it is possible to import each metadata to get the information we need for each post: the title, publication date, and categories. Of course, it is possible to leverage the metadata to pass other information, such as author names, tags, etc. This is just a starting point.
We then create a posts.ts file in /app/lib/ (or wherever you prefer) with the following content:
File: /app/lib/posts.ts
We now have a function that retrieves all of our blog posts so that we can conveniently pass them to a component. Create a new file in /app/components/posts.tsx:
File: /app/components/posts.tsx
As you can see, the component uses date-fns to format the date, so you have to install the package with:
Now that you have this component, you can use it on your pages. A good place might be in your site's /blog/ page. So, let's add a page.tsx file in the /app/blog/ folder with the following code:
File: /app/blog/page.tsx
Categories
Categories are assigned to the metadata of each post. However, to make the code more stable and simple, the categories must be part of a predefined list that you must create. First, let's build this list. In /app/lib/constants.ts we create a constant by adding some categories:
File: /app/lib/constants.ts
And we create the functions needed to manage categories in /app/lib/categories.ts:
File: /app/lib/categories.ts
This file contains two functions: getCategoryById which allows you to find a category using its ID and getCategoryURL which returns the URL of a category, for example /category/javascript.
We then need a function that can return a list of posts filtered by a category. To do this, we need to edit the file /app/components/posts.ts adding this new function:
File: /app/lib/posts.ts
Finally, we edit our /app/components/posts.ts file to show categories under each post:
File: /app/components/posts.ts
As mentioned, we need a way to display a category page using the URL /category/slug. For example /category/javascript. To do this, we can take advantage of Next.js's dynamic router by creating a page.tsx file in the /app/category/[category]/ folder.
As with the post archive, we can leverage the same component to display all the posts in the current category:
File: /app/category/[category]/page.tsx
Important info:
We make use of the generateStaticParams function in the
categories routes to allow them to be generated statically at build time. More
information can be found
here.
Adding syntax highlighting with Rehype Pretty Code
The last step is entirely optional, as it adds syntax highlighting for blocks of code. Of course, not all blogs need this feature, so if you are not interested, skip this step.
First, let's install the necessary packages:
Now that Rehype is installed, we can edit the next.config.mjs file:
File: next.config.mjs
In order to use Rehype, we need to create a new component, so in the /app/components/ folder, add a code.tsx file with the following code:
File: /app/components/code.ts
Through this new Code component, it will then be possible to pass a string and have a block of code with a syntax highlight. For example:
Conclusion
That's it. As you can see, it is relatively easy to create a static blog with Next.js if you have some familiarity with React and modern JavaScript tools. On the other hand, if this is not your case and you want to learn Next.js, I consider the static blog a great first approach because it is not too overwhelming, but at the same time, it allows you to touch some of the main features of this framework.
For access to the complete project, feel free to explore the GitHub repository.