I've been doing a bit of Next.js recently and really fell for Chakra UI.
It does the best job I've seen so far in a UI library at removing the need for CSS (without being Tailwind 🤢).
I didn't have any other UI stuff lined up to work on so thought I'd port this site over to it.
Problem is.
Chakra is a React component library.
For React to have a decent FCP, to work with SEO or to have a social preview, my experience tells me to use Next.js.
But Next.js needs a server and that conflicts with my wallet, which would be much happier with static HTML+CSS on GitHub Pages.
That just won't do. Every developer needs an over engineered personal blog site. Why else would there be a meme for that.
First I looked at Gatsby. Let me share my experience.
npm init gatsby
# Enter -> Enter -> defaults etc
npm init gatsby
# Enter -> Enter -> defaults etc
Nice! They have a really decent npm template. But it did take a while and why didn't I see a vulnerability audit?
cd my-gatsby-site
npm audit # 😱😱😱😱
cd my-gatsby-site
npm audit # 😱😱😱😱
Bearing in mind that this is a freshly templated project, effectively a toy for showcasing the platform,
there were 23 vulnerabilities, of which 1 was critical and 8 were high.
I've worked with some crusty old Node projects and the only time I'd ever see anything like that was when dusting off something we hadn't deployed in maybe a year or more.
I had a look myself and the gatsby package on npm has 168 dependencies.
This is an order of magnitude more than Next.js, which is currently at 16.
Maybe it's just impossible to remain vulnerability free with this many dependencies?
Whatever the case, it seems Gatsby is at meme status on npm for depending on EVERYTHING and so not something I'd fancy maintaining as a dependency.
I read some stuff on Redit about the vulnerabilities not being in the runtime but how would you know?
It'd be like crying wolf with every report.
Plus I just couldn't put the poor GitHub dependabot through it 🤖.
Luckily, it turns out that since I last looked, Next.js has a new router, runs on React server components by default and fully supports static site generation.
That sounds like it'll do the job perfectly then.
npx create-next-app@latest
npx create-next-app@latest
✔ What is your project named? … my-app
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … No
✔ Would you like to use `src/` directory? … No
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias (@/*)? … No
Creating a new Next.js app in /Users/alex/projects/my-app.
✔ What is your project named? … my-app
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … No
✔ Would you like to use `src/` directory? … No
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias (@/*)? … No
Creating a new Next.js app in /Users/alex/projects/my-app.
How relieved was I to see "found 0 vulnerabilities".
Plus unlike Gatsby, I didn't have to run an audit myself.
I like the convenience and portability of writing in markdown.
It takes a bit of setting up in Next.js but I think it's worth it.
Also, since we're using React we may as well use MDX.
Next.js supports rendering MDX with the app router via @next/mdx.
But since a blog site would need a "list of posts" abstraction, a better fit would be MDX remote.
Create a posts folder as a sibling to the app folder and drop a markdown file in with some frontmatter e.g.
---
# posts/my-first-post.mdx
title: My first post
slug: my-first-post
date: 2024-04-09
---
This is my *first* post.
Here's a table.
| Month | Savings |
| -------- | ------- |
| January | $250 |
| February | $80 |
| March | $420 |
---
# posts/my-first-post.mdx
title: My first post
slug: my-first-post
date: 2024-04-09
---
This is my *first* post.
Here's a table.
| Month | Savings |
| -------- | ------- |
| January | $250 |
| February | $80 |
| March | $420 |
Add the dependencies.
# Note remark-gfm is optional if you would like to
# use GitHub flavoured markdown e.g. tables.
# The pinned version are required as of April 2024
# as remark-gfm 4.0.0 breaks compatibility with next-mdx-remote.
npm i next-mdx-remote@^4 remark-gfm@^3
# Note remark-gfm is optional if you would like to
# use GitHub flavoured markdown e.g. tables.
# The pinned version are required as of April 2024
# as remark-gfm 4.0.0 breaks compatibility with next-mdx-remote.
npm i next-mdx-remote@^4 remark-gfm@^3
Next we need to read the posts from disc and compile their MDX into a React cache.
Create this in posts/index.ts
// posts/index.ts
import { readdir, readFile } from 'node:fs/promises'
import path from 'node:path'
import { compileMDX } from 'next-mdx-remote/rsc'
import { cache, ReactElement } from 'react'
import remarkGfm from 'remark-gfm'
interface Post {
title: string
slug: string
date: Date
content: ReactElement
}
type PostFrontmatter = Pick<Post, 'title' | 'slug' | 'date'>
async function getPost(filename: string): Promise<Post> {
// you can override anything here, this is just an example
export const components = {
p: (props: any) => <Text {...props} my={4} />,
table: Table,
thead: Thead,
tr: Tr,
td: Td,
th: Th,
tfoot: Tfoot,
}
To wire this up, in posts/index.ts, pass the exported components object as a property in the object passed to the compileMDX function.
Then replace the bare <h1> in app/posts/[slug]/page.tsx with a Chakra <Heading>. Reload and you should see something like this:
This is just a start, there's plenty you can do with the MDX rendering to properly support styled headings, lists, Next.js client side links, code highlighting, images etc.
Commit and you should ™️ be able to see on GitHub Actions:
If you need a working example, this site is built with Next.js and deployed to GitHub Pages with GitHub Actions from my repository https://github.com/axle-h/axle-h.github.io.