Switching from a live content management system to the most powerful editing experience for technical authors

A good chunk of my background is in journalism and CMS-enabled publishing, so when originally reviving my website I was pretty excited about building with Svelte and a nice headless CMS (I went with Payload). But I was sleeping on this.

My ambition was to extend Payload to support as many custom components and features as I felt I needed to tell stories. I began doing so, but recently switched it up a bit for my own purposes – I migrated to a full Markdown setup for content using mdsvex.

Once I was up and running with a CMS again, it wasn’t long before I realized a few things:

  • Being able to post to my website on the go is nice, but it’s just me – I don’t need real-time posting on the go
  • I’d built a way to enter Markdown into the CMS and I was using it almost exclusively
  • Where Markdown won’t tell the whole story, mdsvex allows me to import and render Svelte components into my normal Markdown content (If you’re familiar with the React ecosystem, it’s like MDX, but for Svelte.) – I was underestimating the value here

Rather than burning my free time customizing the CMS to handle custom data/content and send it over the wire every time I want to create new storytelling functionality, now my content comes from Markdown files that can nest components. And now I can even more easily prerender my pages at build time, allowing them to be served as quickly as possible.

I don’t need to support non-technical authors here. Why spend time doing the extra work?

So now to publish, the process goes:

  1. Write content, including any custom components necessary for the post
  2. Check in the code/content
  3. Build & deploy

Because we’re working with a codebase here, this may not scale well across a typical organization. But for the technically-inclined (and staff who are willing to learn a bit of Git and Markdown), it’s quite powerful.

This move is bittersweet – Payload is everything I want in a developer-first CMS, and it’s my current pick if the choice in CMS for a new project is left up to me – but I know this is the right move for me as a lone technical author publishing on my personal website. Today, I still use Payload for my custom analytics, form submissions and any other data dorkery.

Let’s dive deeper.

New to Markdown? Click here for a quick summary.

As its creator says on his website, “Markdown is a text-to-HTML conversion tool for web writers.”

If you’re an experienced developer or publisher, you know that the tags with which we mark up our pages have meaning for search engines, screen readers and any other bots ingesting the content. So it’s important that the author be able to convey that a headline is a headline, a paragraph is a paragraph, and that a blockquote is a blockquote, etc. Markdown gives us a way to wrap content in the intended tags, but with a simpler syntax that makes the Markdown document itself a bit more human-readable than a fully marked-up HTML page.

It may not sound like much, but it adds up to less typing, and more focusing on the content. The Markdown files can be colocated with the code for the website, and compiled to HTML for use within the website. Usually we just compile them ahead of time so they can be served as static files – no code runs on the server. It just sends the pre-built, browser-ready pages as people request them.

If you were authoring your content directly in HTML, you’d have to write a lot of markup (the ‘M’ in HTML, for the uninitiated) so the browser would know how to display your things. Here’s a quick example:

<h1>This is a top-level header</h1>
<h2>A second-level header, which is almost always styled to look smaller</h2>
<p>Every paragraph I write needs to ultimately end up in a <code><p><code> tag, just like this one. In my CSS, I'll target all paragraph tags to make them look the same.</p>
<p>It can be easy to see how adding all of these tags individually can become messy. It's not something we often do manually. <a href="https://google.com" title="Check out google.com!">Links can be particularly cumbersome</a> if you need a lot of them.</p>

We could just drop that HTML into a page and publish it. But by using markdown, then ‘compiling’ it into HTML in a separate step, we can more cleanly author and edit our content in a more human-friendly syntax. Here’s the Markdown that would produce the above HTML:

# This is a top-level header
## A second-level header, which is almost always styled to look smaller

Every paragraph I write needs to ultimately end up in a `<p>` tag, just like this one. In my CSS, I'll target all paragraph tags to make them look the same.

It can be easy to see how adding all of these tags individually can become messy. It's not something we often do manually. [Links can be particularly cumbersome](https://google.com "Check out google.com!") if you need a lot of them.

If you’re new to Markdown and want to try it out, Gruber’s website is a good place to start.

Being able to use Svelte within markdown files allows me to tells a story however it needs to be told

As I mentioned already, one of the main benefits of this approach is the ability to pull in custom components to compose a page or tell a story. This includes more corporate, business-objective-ish things such as:

  • newsletter signup forms (or other forms, such as surveys)
  • ads in the middle of the story (we love those)
  • calls to action
  • dataviz/graphs
  • calculators

You get the idea – if you can cobble it together with JavaScript, HTML and CSS, you can easily use it right alongside youre prose. If I want to create some sort of contrived, interactive pizza calculator and pull it into this post, nothing is stopping me.

🍕 How much do you like pizza?

50% Pizza

Based on my highly-scientific calculations, you are approximately 50% pizza.

Maybe I’m writing an entire post on Pizza, and want to present my personal findings with Chart.js.

I inlined the dummy data for that graph, but I can just as easily request data from elsewhere. If I were writing a soft piece about how great Cincinnati is, I could pull the local brewery data from openbrewerydb.org and present it in a little widget, right next to my content.

Tomorrow is Thanksgiving here in America. What if I’m feeling festive, and want to cut a turkey loose across my page, right here in between paragraphs? Hold my beer.

You get the idea.

For slow-churn data, you can build and pre-render simple static APIs

With this approach, our Markdown files are serving as the data source for our content. Joy of Code has a fantastic post about building with mdsvex, and it contains a great explainer on building an API endpoint for your content, which can be used elsewhere on your site, or externally. Endpoints can also feasibly be prerendered at build time, making them even faster.

Imagine you’re building a website for a private software company that has a portfolio of products, each with version numbers, price, product name, description and other data that need to be updated upon release. Elsewhere on your site, you’d almost certainly mention the product version number and price, and want to update it globally. Maybe partner companies and resellers want to be able to check for new product releases so they can update their own websites, or develop their own timely marketing push for your products.

With content and static data releasing in tandem with the rest of the website’s code, we can cover all our needs, and update everything in one simple code release.

This could all be done with arbitrary content/data in the frontmatter, and the actual Markdown content could even be used to house release-specific content, such as the marketing-cleansed changelog, or other release details to use on a page.

It’s also worth mentioning that this can be done with different types of static files, such as CSVs, and can be combined with the more typical content delivery and ingestion strategies. If you need to support ongoing, real-time publishing (say, for the blog and other pages), you can get the best of both worlds. And if you have no need for the content part, Vite makes it trivial to import JSON files for the same purpose.

Proper caching strategies in front of dynamic (i.e., they pull data from databases or other data sources) API endpoints probably makes more sense in most cases, but for slow-churn content, static files as a data source aren’t quite as crazy as it may sound at first.

Looking forward: Your Markdown content can easily go with you when it’s time to rebuild

It’s inevitable – eventually you will want to build a brand new website, and you’ll likely be switching some of the underlying tech and strategy. Maybe you want to get away from Markdown, migrate back to a CMS, to the CMS’s database via an import or lower-level migration.

I’ve seen some ugly data migrations over the years, but if I were beginning one today, I think I’d be happy to see that Markdown files were part of the source data that needed to make it in.

There’s always a risk that malformed data has made it into these files (humans ‘gon human), but when developing your .md file against a running local copy of the codebase, the feedback you receive as you compose is immediate. You see how it’ll function live, in production and all lower environments. If you messed up badly enough that the site will crash on your Markdown file, the only way you won’t know is if you (or someone else) are not testing your work.

Migrating away from this setup once it’s become the wrong choice for your organization would probably look different depending on where/what you’re moving it to. You could build an API for the new system to consume, or extend the new one to do the importing for you. But this also highlights a potential footgun.

If you were using a lot of custom components in your content, you’ll need to either sort out how to handle those in the new system, or forget about them entirely. For what it’s worth, most CMSes worth using have developed some sort of way to do this, some directly in the rich text editor.

I was undervaluing this approach at first

MDX has been around for a while, and I knew it made components-in-markdown a thing, but I was always placing more value on being able to post from anywhere, at anytime.

I’d done a lot of thinking about it over the years, but I’d always come up with a reason to stick to my CMS-y ways. Over time I’d read about startups choosing Docusaurus for their website and documentation, leading to much thought as to why.

I think one reason for this is similar to my own: To minimize the work you must do to publish anything, no matter how atypical. For a technically-inclined solo author or small team, I don’t think you can beat this, unless you simply don’t need or want the expressiveness this nets you.

But you may be able to see how this might not scale well for growing teams. New hires inevitably happen, and not all marketing and creative folks will feel great about learning Markdown and the basics of Git.

©2024 Joe Castelli