For most of last year I had been thinking about moving from Python to JavaScript, especially because I wanted more room to improve the front end. The previous version of the blog was still relying on an old CDN-based way of importing modules, and that made a lot of new features awkward or impossible to keep building. So instead of stretching the pain out, I decided to rebuild it.

What I Wanted From the Rebuild

The requirements were actually simple.

I wanted one command to create a new empty .md file for a post. After finishing the article, another command should publish it to the server. Everything else—packaging, compilation, static asset updates—should happen automatically.

For the front end, I wanted a good-looking UI component library so it would be easy to experiment with new features. For the server, the requirement was also very clear: it had to be free, because that has always been the tradition.

Choosing the Stack

For the main front-end direction, the mature choices were basically Vue vs React. I chose React.

As for why, I find React’s component and state model easier to understand, and writing it feels closer to native JavaScript. Vue has a lot of syntactic sugar, and although many people like that, I personally never got fully comfortable with it. Of course, this is mostly a matter of taste.

Once React was decided, Next.js became the obvious choice for generating static pages. The matching deployment platform was Vercel. The whole flow is very straightforward: write the code, push it to GitHub, let Vercel listen to the repository, pull the latest code automatically, build the static assets, and deploy them. After that, I only need to point the domain name correctly.

Most of the other framework-related choices were built around the Next.js ecosystem.

"dependencies": {
    "@giscus/react": "^3.0.0",
    "@nextui-org/react": "^2.6.10",
    "framer-motion": "^11.15.0",
    "gray-matter": "^4.0.3",
    "next": "15.1.2",
    "next-themes": "^0.4.4",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-markdown": "^9.0.1"
},
"devDependencies": {
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "tailwindcss-motion": "^1.0.1"
}

These are the main dependencies used in the project.

  • @giscus/react ^3.0.0: a React component library for embedding Giscus, an open-source comment system based on GitHub Discussions. It makes it convenient to add comments to a site, while storing discussion data inside a GitHub repository.

  • @nextui-org/react ^2.6.10: NextUI is an open-source React UI component library. It provides components such as buttons, cards, inputs, and modals, with a modern look and a good user experience. It also supports theme customization and can work well with tools such as Tailwind CSS.

  • framer-motion ^11.15.0: an animation library for React. It offers a powerful but relatively simple API for building animations, including enter and exit transitions, scroll-based interactions, click-triggered animations, chained animations, and custom easing functions.

  • gray-matter ^4.0.3: a library for parsing front matter from files. In a static blog, metadata such as title, date, and author is often stored at the top of a Markdown file. gray-matter extracts that data and turns it into a JavaScript object for later use.

  • next 15.1.2: Next.js is an open-source React framework that supports server-side rendering, static site generation, incremental static regeneration, routing, and other built-in features. It improves loading performance and SEO while also providing conveniences such as hot module replacement during development.

  • next-themes ^0.4.4: a theme-switching library for Next.js projects. It makes it easy to add light and dark mode, supports dynamic theme changes, and can also follow the user’s system preference.

  • react ^18.3.1: the core JavaScript library for building user interfaces. React uses a declarative and component-based model, which makes UI code easier to maintain and reuse.

  • react-dom ^18.3.1: the DOM binding package for React. It renders React components into the browser DOM and works together with React to update the interface when component state changes.

  • react-markdown ^9.0.1: a library that renders Markdown text as React components. It converts Markdown syntax into the corresponding HTML structure and supports customization through plugins, such as code highlighting or custom link handling.

  • postcss ^8: a tool for transforming CSS with JavaScript plugins. It can add browser prefixes, compress CSS, transform syntax, and is often used together with Tailwind CSS.

  • tailwindcss ^3.4.1: a utility-first CSS framework. It provides predefined classes that can be combined directly in markup to build layouts and styles quickly. Its atomic approach reduces repetitive CSS, and its configuration file allows customization of themes, colors, spacing, and more.

  • tailwindcss-motion ^1.0.1: a Tailwind CSS plugin that adds animation features based on framer-motion. It lets animation-related classes be defined in Tailwind configuration and used like normal Tailwind classes, combining Tailwind’s convenience with framer-motion’s animation capabilities.

The Core Part

The most important pieces are getStaticPaths and getStaticProps, two methods provided by Next.js for static resource generation. Combined with gray-matter, they are enough to generate a React-based static page for every blog post.

getStaticPaths is responsible for collecting the paths of all blog posts. getStaticProps, together with gray-matter, parses the front matter of each Markdown file and extracts the information needed for the post.

With this part in place, most of the blog system is already solved.

The funny thing is that in the previous version, I wrote my own static blog generator in Python. I reimplemented all of these features by hand, and I was actually very enthusiastic about it at the time. Looking back, that kind of stubborn energy was a little silly, but also strangely exciting.

Other Blog Features

Most of the remaining basic features were built around the same core methods. With help from AI, the core functionality took roughly ten hours in total if counted by working hours.

For the front end, I used NextUI. The overall theme and style of the blog did not change much from the previous version, though some details may be different.

Sitemap and RSS

A blog should not be without a sitemap and RSS.

Next.js has a sitemap module, but it could not automatically generate what I needed during server deployment. So I wrote two small scripts myself: one for generating the sitemap XML, and one for generating the RSS XML.

Before pushing the project to GitHub, these two XML files are generated into the public directory. Once they are there, Vercel can deploy them automatically together with the rest of the static assets.

Comments

I did not plan to write my own comment system for now.

On one hand, I do not have much experience building comment systems. On the other hand, there is also the pressure of moderation and management. After weighing the trade-offs, I chose Giscus.

Its features are enough for this blog. Besides, this site rarely gets comments anyway, and most visitors are probably programmers, which means they usually already have GitHub accounts. So Giscus ended up being the most suitable choice.

A Small Record

Writing all this down may not have much practical value, but if someone else is also using Next.js to build a personal blog, these notes might offer a small point of reference.