Rebuilding my blog with Next.js
I use my website to try new technologies more than writing content, this means that I’ve rebuilt my site more times than I’ve written articles. This time I’m rebuilding it using Next.js, and if you’re reading this, I already deployed the first version.
Why Next.js
With the rise of React and the SSR frameworks, e.g., Gatsby and Next.js, I saw a lot of beautiful, fast blogs built with Gatsby. You probably have already seen Dan’s. Of course, I went and tried to set up my own, but the thing I didn’t like was that Gatsby blog-starter requires GraphQL, which is good, but I don’t think I need that for a simple blog.
So with the bad habit I have of wanting to build my version of everything, I started to investigate how to build a blog with Next.js.
How
I got familiar with Next.js by contributing a couple of examples and other small changes. I’m also currently building two products with it, so I felt confident that I could build something decent.
Checklist
I started with an empty project using create-next-app, and from there, I made a list of the features I needed.
- Generate a list of posts from MDX files
- Add syntax highlighting
- Add an RSS feed
- Add pagination
For a Next.js project, all these things were new for me, and I had no idea how to implement them. So, I went to investigate how other people are doing it.
Guillermo Rauch (@rauchg) told me that Max Stoiber’s site (@mxstbr) was one of his favorite blogs built with Next.js out there, so I looked at his GitHub repo.
Max has 80% of the features I need in his repository, so most of the credit for the work goes to him; his implementation is pretty smart and helped me a lot. I was fortunate to chat with him about it too.
1. Generate a list of posts from MDX files
The goal is to use Node to read the contents of a folder and get a list of files, then for each file, create a JavaScript Object with title
, content
, and other metadata.
To use the file system module at build-time, Max used babel-plugin-preval/macro; this package lets you run Node code dynamically in a client context and export the results.
With Max’s implementation we can import the list of posts and iterate through the Object
, e.g.,
import blogposts from "../../data/blog-posts";
function Blog() {
return (
<ul>
{blogposts.map((post, index) => (
<li key={index}>
<a href={post.path}>{post.title}</a>
<time>{post.publishedAt}</time>
{post.summary}
</li>
))}
</ul>
);
}
export default Blog;
2. Syntax highlighting
Syntax highlighting was harder than I thought. Basic syntax highlighting worked with rehype-prism, but one key feature was missing, the ability to highlight a line of code, e.g.,
import React, { useState, useEffect, useRef } from 'react';
function counter() {
let [count, setCount] = useState(0);
setInterval(() => {
// Look, I'm highlighted!
setCount(count + 1);
}, 1000);
return <h1>{count}</h1>;
}
Adding line highlighting was probably the hardest part of the process. To implement it, I went to a rabbit hole of learning about unified.js and how syntax trees work. I had to understand how the Gatsby team and contributors implemented their own and how to plug it into the MDX plugin interface.
I stole code got inspiration from these packages:
I won’t go in detail, but I integrated code from those three packages to get syntax highlighting working along with the line highlighting feature. Other features are missing but got it’s working for now. Since I’ve created a fork that adds line highlighting to rehype-prism.
3. RSS feed
This feature was easy. I followed Max’s lead in using the JSON feed spec and reformatting the blog post Object
into a JSON feed.
I still haven’t figure out how to create the JSON file on build time, so, for now, I’m running the node script before committing changes to generate the feed and routing it as a static file.
An npm script runs a Node function that generates the feed as a static file when it’s deploying in Now.
// package.json
"scripts": {
"dev": "next",
"build": "next build",
"build:rss": "node ./.next/serverless/posts/rss-feed.js",
"now-build": "next build && yarn build:rss",
"start": "next start",
"test": "jest"
},
4. Pagination
This feature was easy, too. I used the pagination library with the blog posts Object
’s length and other options as input.
Development environment
One of the benefits of using Next.js with Now 2.0 is the now dev
command. You get to see what you will get in production. It uses the same now.json
configuration file and pretty much everything else works the same way.
Deployment
After all the work, I got the project into a good-enough working blog using Next.js. At this point, I was really excited to have it deployed, and that Saturday night I decided to launch it using Now 2.0.
Conclusion
The website feels fast. The Lighthouse audit results are amazing. The easiness of adding content feels as if you were dealing with a CMS, except there’s no login.
The SSR and pre-fetching features Next.js provides makes the site feel very smooth, fast and responsive.
So far I’m pleased with the experience of developing with Next.js and Now. I will submit a pull request to the Next.js repository to add the blog as an example, and I hope somebody will find this work useful as I found Max’s.
Also published in DEV.to.