Creating my blog using Next.js

  • Next.js

Written on

I've decided to kickstart my sabbatical by revamping my website from scratch and creating a blog. In this post, I explain why I created it, and the technical decisions I've made while developing it.

Motivations and goals

Having my place on the web, where I can showcase my work and share articles and opinions is important to me. I've learned a lot from other people's blog posts and articles, and I'm trying to write more, as a way to structure my thoughts and share knowledge with others. Writing forces me to research and think deeply and, by putting it out there, it allows other people to benefit from it, and for me to receive feedback: either validating my ideas or discussing alternatives.

I wanted this first version of my website/blog to be simple, readable, and allow for quick iteration. This was inspired by the concept of Incremental Correctness:

Incremental correctness is the process of iterating towards something more truthful, accurate, usable, or interesting. The faster we can iterate, the faster we can discover good ideas. Things aren't perfect today, but tomorrow things can be slightly closer to perfect.

Using React for a static site

I knew that my website and blog was going to be static, without dynamic content (generated by a server, or requested from an API).

React and Static are almost opposite in semantics. A few years ago I would have defended using plain HTML for your static website, as React is intended for reactive applications. While React was built (and it's really good) at supporting that use case, its declarative, component-based architecture makes it easier to reason and structure your website into small reusable components.

React also became quite good at generating static HTML pages, that can be delivered right at the Edge, next to the users, via a CDN. Frameworks such as Gatsby and Next.js make this setup and workflow easy, providing an accelerated development experience.

There's nothing wrong with using plain HTML/CSS/JS, or any other tool for your website and blog. Just go with the tool that will better accomplish your goals, whatever they might be.

Going with Next.js as a base framework

I've been exploring Next.js lately, as the base framework for a full-featured web application. It provides interesting features out of the box, such as Universal Rendering (server-side rendering using the same React components that are used in the client), and Static Site Generation, which builds pages (using React components) as static HTML. This allows me to create a fully rendered static HTML page that's served at the Edge while maintaining the SPA experience, which makes navigating between pages very fast.

What I'm liking about Next.js is how decoupled the framework is from your codebase. It is practically unopinionated on how you structure your source code (apart from inferring your routes through the file system, which can also be customized) and the coupling pieces are just on a few hook points (such as getStaticProps) and methods/components (such as Link or useRouter), that could be abstracted away to promote easier decoupling. This makes it relatively easy to opt-out and to change it to another React-based framework if I ever feel the need to do that.

Just like create-react-app, it allows you to not have to think about tooling (is: webpack) and build configurations while allowing for customization. Deploys are also straightforward, especially with a provider like Vercel (which are the creators of Next.js) or Netlify. You can use any other provider, as it generates a .next directory with all the assets that need to be deployed.

If your goal is to simply have your site/blog up and running, my suggestion is to use Wordpress or Gatsby instead. Next.js is a more generic framework, and while it's certainly possible to create your blog in it (in fact, the official tutorial explains how to do it), it's not as easy/quick as with Wordpress or Gatsby.

Initial Setup

The initial setup for a Next.js app is straightforward (especially if you know React already). I highly recommend reading and following along the official Learn tutorial, which makes an excellent job explaining how to write your blog using Next.js.

My blog has a few differences or features that were not covered by the tutorial that I will highlight in the next sections.

Separating concerns

I started building this website by modeling the UI components for the header, navigation, and footer. Since these will always be the same, regardless of the page they are in, I've included them on _app.js. By doing this, the components won’t be re-rendered when navigating between pages (only the content will be fetched and rendered). All UI components are pretty much “dumb”, in the sense that they only render simple properties, without doing any computation (the exception being the BlogMarkdown component, that executes a processor, by using a custom React hook).

By separating business logic (in this case, the logic related to Blogs and Blog Posts) from the presentational components, it makes the code easier to read, maintain, and test. I also made sure dependencies were properly encapsulated, which are injected into the entities that need them. If I ever want to create a native GUI, or CLI to replace the React components, the logic for blogs and blog posts should stay pretty much untouched, as long as the same interfaces are fulfilled. The same can be said for the logic for retrieving posts (currently using the file system, but I could refactor it easily to use a headless CMS, for example) and for processing Markdown.

Discovering blog pages

The tutorial retrieves blog posts by reading the posts directory on the file system. I decided to have a manifest file on the root directory of my blog directory. This enables me to have “drafts” (I can have Markdown files that are still a Work In Progress on the directory, that are not published), and control the ordering of the blog posts (I could also have used the date attribute on the metadata to achieve this).

At first, I thought this approach would be more efficient (since we only read a manifest.json instead of going through the directory and parsing every blog post in it), but looking back at this choice, I realized that this only affects the build time and not the user’s load time in any way, so I might reconsider and use the same approach on the tutorial (I can add a draft attribute on each blog post that is a draft, to filter those out from publishing).

Use of marked and highlightjs

Instead of using remark (which is used by the Next.js tutorial), I've decided to use marked. I also changed the code renderer on marked to render highlighted code, using highlight.js. There was no particular reason for me to choose marked. I chose it because I started working on this blog before the tutorial was released, and marked was the first option that I researched.

I debated on whether I wanted the blog contents to be served in Markdown format and then rendered in HTML in the client, or serve it directly as HTML. With static generation that produces no difference in the HTML output, but when navigating between pages in the client, it will.

With the first option, it will fetch the raw markdown in a text format, on the second, it will fetch it already rendered as HTML. The second option will produce more output (all the HTML tags), so I've chosen the first option, to minimize the network payload when navigating between pages.

To accomplish this, you make use of getStaticProps:

// this will fetch the raw markdown content, for later processing
// by the React component
async function getStaticProps() {
  const rawContent = await getContent()

  return {
    props: {
      content: rawContent,
    },
  }
}

// this is the component that will process it
// static site generation will build it as static html
// but if you're navigating, it will be executed in the client
function MyComponent({ content }) {
  const htmlContent = processMarkdown(content)
}

To render the HTML in the client (the second option), you can do the following:

// this will fetch the already processed HTML content
async function getStaticProps() {
  const content = processMarkdown(await getContent())

  return {
    props: {
      content,
    },
  }
}

A lack of pagination

You might notice that I haven't implemented pagination. I only have 3 articles on the blog, so I don't need it yet. This fits into the Incremental Correctness principle I mentioned previously. The current truth of this blog doesn't demand pagination yet, which gives me time to focus on other things and release earlier. The best part is that I don’t need to decide on what kind of pagination I need to implement until I need it. For example, is it better to simply show 5 posts per page? Or remove the excerpt and just show the titles? Or separate by tags, or by year?

As a related anecdote, Basecamp was first launched without a billing system, because they knew they had 30 days until they needed one.

A note on Typescript and CSS

I’ve chosen JavaScript and CSS Modules because they worked fine for this use case (small codebase, one developer). However, I started to feel the itch for Typescript when I was defining what a processor and loader were (I like to start by defining the interface, to find the common traits and make sure I stick to a common contract). Next.js has built-in support for Typescript, so I might consider moving to it in the future.

As for CSS, I’ve been reading good arguments for using CSS-in-JS. Traditionally, this is something I’ve frowned upon, as I defended that Styling is a concern that should be separated from HTML and Javascript. However, JSX has already fused the markup concern with Javascript behavior. By including styling, the Component is effectively a complete unit of UI. In that sense, I’d like to explore using emotion for it.

Future Work

For now, my concern will be on adding more content, which I believe is the single thing that can make this blog/website successful. I might keep playing with the navigation on the blog and styles, following the principles of Incremental Correctness, going one step at a time, closer to the truth.