Building a Blog with Next.js, Tailwind, & MDX
There's an intimidating number of excellent front-end tools.
Let's dive into my answers to these questions.
Choosing a Stack
I've landed on a stack that offers an exceptional developer experience, crazy fast load times, and the flexibility to create rich and interactive client-side experiences.
This blog post will break down the "why" of each of these technologies.
Want an example? Check out the source code for this very blog — it's open source!
Next.js calls itself "The React Framework for Production," and for good reason.
The React ecosystem is flourishing right now. The library has grown tremendously in the eight (!) years since it was initially released, as has the community surrounding it.
Let's talk about that.
What's special about Next.js?
Next.js provides a suite of features & conventions around React that greatly simplify the process of building a web application. Some standouts include:
- Static generation & server-side rendering
- Zero configuration (no custom Webpack or Babel configs)
- Built-in client-side routing & image optimization
- Dead simple serverless API creation
- ...and more!
Out of the box, React doesn't have an opinion on how your application is structured. Next.js provides sensible defaults that get you out of the business of making decisions about boilerplate. I've written a lot of custom Webpack configs, and it's rarely how I'd like to be spending my time.
Next.js's biggest competitive advantage, in my view, is the way that it approaches server-side rendering. To begin with, every page is pre-rendered. This means that the markup for each page is generated before it ever gets to the user's browser. There's no waiting for React to load and construct the DOM from scratch.
Crucially, it allows for both static generation and server-side rendering on a per-page basis. This is truly the best of both worlds.
For static content (e.g. this blog post), Next.js can statically generate the page at build time, then immediately cache the result for Jamstack-level performance. For dynamic content (e.g. a user's personal settings page), there are several options for dynamically generating the page at request time.
What about other React Frameworks?
Each of these frameworks are useful, but Next.js retains an advantage in several key areas.
Next.js vs Gatsby
It's a great tool, and I've used it for several projects. There's a rich ecosystem of plugins, and getting a project spun-up is quick and easy.
The major advantage of Next.js over Gatsby is server-side rendering at runtime. Gatsby generates everything at build-time, and can't generate dynamic pages on-the-fly. Dynamic data must be fetched on the client. There are many scenarios where this is inconvenient.
Moreover, Gatsby is heavily reliant on GraphQL. This isn't necessarily a bad thing, but Next.js has the benefit of being agnostic to how the data layer is managed.
Next.js vs Create React App
Create React App is a framework for quickly scaffolding out new React single-page applications. It takes care of boilerplate build configuration and dependencies.
Instead of writing rules like
display: flex; and
border-radius: 4px, Tailwind exposes utility classes like
Tailwind also helps to codify your design system — instead of littering your CSS with arbitrary hex color values Tailwind exposes classes like
I love Tailwind.
Why not just write CSS?
At first, the extra clutter and mental overhead of utility classes seemed like a bad idea. Then I tried it.
In reality, being able to spend all of my time in the markup (
JSX) feels really intuitive.
This whole website has essentially zero custom CSS. There's no need to worry about how to scope or architect the CSS. There's no need to worry about the potential runtime overhead of a CSS-in-JS solution.
Everything is a utility class, and making tweaks is dead simple.
MDX allows you import React components directly inside of Markdown files.
This is a total game changer.
Markdown is by far my favorite way to author blog posts and documentation — it's portable, largely standardized, and a natural fit for Git workflows. But on its own, it's cumbersome to include rich or interactive markup.
MDX combines the ease of writing Markdown with the flexibility of custom React components.
For example: MDX allows me to easily include the
next/image components directly within blog posts, enabling client-side routing and image optimization that would otherwise be unavailable.
I'm also using the excellent
@tailwindcss/typography plugin to style the raw markup that MDX generates.
There are several ways to integrate MDX with Next.js.
@next/mdx plugin is the easiest to configure, and it supports the same file-based routing that comes built into the framework. Just throw a
.mdx file in the
/pages directory, and you've got yourself a page.
/pages directory — it's not possible to source the data from anywhere else.
There are several excellent free options available for hosting a Next.js application.
I've chosen to host this blog on ▲ Vercel, the makers of Next.js. The developer experience on the Vercel platform is second-to-none — plus, it's free for personal use! Deploy previews are a game changer, and the Git integration is seamless.
Next.js can be deployed on Vercel with no configuration, and the platform automatically handles scaling, analytics, cache invalidation, and API deployment.
Netlify has a similarly strong developer experience, though it cannot claim the same first-party Next.js support held by Vercel.
AWS Amplify, while quite functional, is notably behind both Vercel and Netlify in terms of developer experience.
I'm really happy with the Next.js, Tailwind, & MDX stack. If you're working on a project in this space and I can be of assistance, please feel free to reach out!