How this blog is built
For the impatient: I'm not sure MDX is worth it, for reasons I'll describe below.
I set out recently to create this blog, and although I didn't know exactly where it would take me, I had a rough idea of the features I wanted from whatever tech was involved:
- A familiar authoring experience
- Static generation
- Easy deployment
Before making specific technology choices, I considered these points in the broader context of what I need, and what I hope will be the easiest to maintain over time.
Sometimes, I just want to write
I've started blogs before, and gone down the path of setting up Wordpress, or building a full directory of .md
files, or whatever. Inevitably I burn myself out shaving the various yaks, and never end up writing much.
This attempt may yield similar results, but I'm trying to build something that lets me drop in when I'm motivated and publish before my inspiration fades.
Static output FTW
I really don't want to worry about uptime, or security, or any of the other operational concerns of managing a web property. I'm good enough at my day job to know that I'm not up to the challenge of keeping a complex personal system running smoothly.
Static site generators are easy peasy (if not the setup, certainly the maintenance)... nothing beats a build artifact that is simply a directory of text-based files: it can be recreated or redeployed easily, and it requires no specific permissions or infrastructure at run-time.
CDNs and table stakes
You're probably aware of the cliché that serverless
really just means someone else's server
, but static file serving is definitely a commodity space.
It's become so easy and cheap to delegate that I can't see a good reason to manage it myself (there is nothing I can do to solve this problem better)... avoiding an obvious foot-gun is satisfying, getting proper caching and HTTPS for free is just icing on the cake.
Why Next.js and Netlify?
I've been developing React apps professionally for a while, and Next.js is definitely solving some difficult problems. The potential for server-side code if I need it, combined with static-site-generation seems to offer a good balance between "room to grow" and an easy dev loop.
I looked briefly at Gatsby, but decided on Next.js due to what I perceived as a lower net-complexity level overall. It's quite possible I didn't spend enough time on the evaluation, but after reading the docs and scanning the source of some blogs I happen to know are built on Gatsby, it seemed overkill for what I need.
As far as hosting, I've been wanting to try Netlify for a while. Several of my internet heroes work on it, and I've heard really positive things. Their free tier is more than enough to get started with, so off I went.
Vercel — the company behind Next.js — also has a hosting platform that was tailor-made for Next.js. In truth, I didn't spend much time evaluating it: after some quick Googling and a comparison of the pricing pages, I'm convinced that both are sufficient, and it'll be easy to change if the need ever arises.
With my choice(s) made, I began to work out how things would fit together. There are a few concerns to manage:
- Metadata for each post
- Content format
- Syntax highlighting
Post metadata
Regardless of how other things play out, I want to be able to associate some related data to each post: a published date, whether or not it is in draft status, a list of topics it covers, etc.
If I were willing to run a back-end as well, these would likely be database fields alongside the content. Since I'm not willing however, I need a way to store this data in source control, alongside the content it relates to.
This is one of my least-opinionated concerns: anything that gets the job done is probably fine.
First formatting attempt: Markdown
Markdown was a natural starting point for authoring pages / posts: it's basically plain-text, but offers a subset of formatting to bring the content closer to HTML. I was able to get some basic syntax-highlighting working as well, using react-syntax-highlighter. By adding front-matter I was also able to solve the problem of metadata: alongside each post I could embed the relevant information. A sample post in this setup would look like:
As I started writing more content though, it became clear I'd need something more powerful to support the level of interactivity I want in some posts: beyond just code snippets, I want to be able to write client-side behavior easily. Although most of the content here will benefit from static rendering, I have a few things in mind that will require React on the client as well.
Second formatting attempt: MDX
MDX was an obvious evolution from Markdown: in addition to simple formatting syntax, I'd be able to directly embed React components where needed.
If you're not familiar with MDX, its basically React and Markdown smashed together and it looks like this:
It's really popular in the React community, and I wanted to love it.
I got MDX set up, and with next-mdx-remote I was able to get syntax highlighting as well, but the cracks in my approach were starting to show: I had to provide a mapping of components during rendering, so that specific entities (e.g. code blocks) could be handled properly. Further, because of how re-hydration works for next-mdx-remote
, the rendered output was ending up in props for the page and in the HTML, effectively doubling the size of each page for no real benefit.
I think MDX is great for documentation, component stories, and probably lots of other things. Realistically, most of the problems I ran into are more likely a symptom of my lack of understanding rather than an indictment of the tools involved. Nonetheless...
MDX feels like a leaky abstraction
I know people love MDX, and I'm happily using it on some other projects. But when setting it up for this blog, I realized a few things that gave me pause to consider alternatives.
MDX mixes two complex grammars: JSX and Markdown. Each of these "languages" (DSL is probably a more appropriate term?) requires an understanding of the abstraction involved, and at very different levels. To work with both at the same time requires a level of cognitive overhead that makes me uneasy.
Further, while MDX was pretty easy to set up, something about the developer experience just doesn't sit right with me... the rest of this blog is written in Typescript, and I've come to really appreciate using both Prettier and Eslint. The tooling around MDX isn't great from what I've seen, and although I'm sure it's possible to find or write the various plugins / parsers to make this better, remember from above that I'm really trying to avoid building thesis-grade integrations for a simple blog.
I'm sure these problems are solvable, but the complexity was starting to pile up. I was left wondering if the convenience of Markdown was actually worth it.
What's the answer, then?
I landed on a final solution pretty close to where I started: React components all the way down. Every bit of content on this page (and the others) is written in React: one component per page, with shared components extracted to a common folder.
This makes the tooling I've come to enjoy incredibly simple: because everything is written in Typescript and React, the entire site can take advantage of static analysis. I get lint errors when content is malformed. Prettier fixes style issues automatically. I'm mostly free to just write.
For a mostly-static blog React isn't needed on most pages (at run-time). That's not a problem though, because Next.js is statically rendering all the pages at build-time, and only re-hydrating the content that needs it. Put another way: React is an implementation detail unless or until I choose to break free and embed dynamic content.
If a future arrives where React no longer makes sense for this project, JSX seems like as reasonable a source format to migrate from as anything else.
Syntax-highlighting with React
There were still questions to answer, though. One of the main benefits of Markdown and MDX was inlining code samples. There is an undeniable simplicity to this:
Because I'm now exclusively in React, the solution is to lean in and write a component for this (I chose to use and wrap prism-react-renderer). By treating the actual code snippet as a single string, code blocks are written like this:
...and it renders on the page like this:
Yes, inlining string literals mean that code samples still aren't linted, but that's a trade-off I'm willing to live with considering that all the rest of the content is.
The problem of handling metadata for each post becomes trivial as well: each post is defined as a default export for the content, and a separate (typed!) export for the associated data. For example, this post you're reading is defined like this:
There is also a real benefit in this approach that is perhaps less obvious: the dependency tree is surprisingly light.
Because I don't need Markdown or MDX parsing, or any of the additional packages that come along for the ride, I'm avoiding a ton of potential maintenance.
Static content, dynamically generated at build-time
At build time, we scan the posts/
directory and do some analysis:
- Collect all the topics for all posts
- Define a static route for each topic, with logic to filter posts accordingly
- Define a static route for each post
This lets Next.js produce a fully-static site: content is output as HTML and then re-hydrated if necessary. If you're interested in how this works, it's well-documented in the sections for getStaticProps and getStaticPaths.
Thanks to Netlify, I just need to git push
changes when they're ready, then wait for the build to finish. The Netlify deployment docs tell a better story than I can, but it's essentially running next build && next export
when a change is detected, and serving the build output from the (CDN-backed!) preview host that they provide.
It currently takes about a minute to build and deploy, and given that the Netlify free tier includes 300 build minutes per month, I can opt to push and build roughly ~10 times per day at the moment. For free.
I can't stress enough that this workflow is incredible, considering how unimportant this project is in the grander scheme of things. At least for now, no one but me cares about how this all fits together, and I realize I'm lucky to be able to take advantage of these tools given how easy they make self-publishing.
Here's hoping that the prices stay low, the tech stays stable, and the urge to write stays with me. Thanks for reading!