Jeffrey Emanuel
March 30, 2024
9 min read
I recently needed a blog for my recent Next.js app, YouTube Transcript Optimizer, and wanted something that would look really nice and that could be integrated into my existing Next.js app to keep deployment simple and to give me more control over how it's hosted and configured.
My goal was to get something that looked very slick, using modern CSS styling and rich client-side effects that would look great on desktop and mobile, and most importantly, something that would be very easy and convenient for me to create new blog posts and edit existing posts. You may remember seeing my first real blog post made using this system, since I recently posted it to HN.
I realized that it could be useful to other people, so I decided to separate it from the rest of the my application into a standalone open-source project that can be easily integrated into an existing Next.js app to add blogging functionality, or just by itself. In this article, I'll explain how it all works and why I made it.
The core idea is straightforward: write Markdown files, push them to a GitHub repo, and let Next.js handle the rendering and delivery. It's turned out to be a surprisingly elegant solution that's both powerful and maintenance-free.
The system is built around a simple concept: your blog posts are just Markdown files in a GitHub repository. Each file starts with a YAML frontmatter block containing metadata:
---
title: "Your Post Title"
date: "2024-03-30"
excerpt: "Brief description"
category: "Web Development"
tags: ["Next.js", "Tailwind CSS", "Blogging", "GitHub"]
coverImage: "https://raw.githubusercontent.com/Dicklesworthstone/yto_blog_posts/refs/heads/main/blog_01_banner.webp"
author: "Jeffrey Emanuel"
authorImage: "https://pbs.twimg.com/profile_images/1225476100547063809/53jSWs7z_400x400.jpg"
authorBio: "Software Engineer and Founder of YouTube Transcript Optimizer"
---
Your content here as markdown...
You can see what this looks like in practice using my last blog post which was linked above; here is the corresponding markdown file for that blog post.
When the Next.js application builds, it:
The resulting pages are statically generated at build time, meaning they're nice and fast and can be hosted anywhere that serves static files.
The core functionality is built with:
The system architecture has three main components working together:
First, there's the GitHub repository that serves as our content store. This is just a collection of Markdown files, each representing a blog post. The files include YAML frontmatter at the top with metadata like titles, dates, and tags. You can also include images in the repo, or use a CDN or other host.
Second, we have the content fetching system. Using GitHub's REST API, we first get a list of all Markdown files in the repository. For each file, we then fetch its raw content. The system can optionally authenticate with GitHub using an access token (for private repos) and handles the API responses appropriately.
Once we have the raw files, we process them: the frontmatter is parsed using the gray-matter
library to extract metadata, and the main content is processed through our Markdown pipeline. The posts are then sorted by date, with the most recent first.
The Markdown processing is handled through a pipeline built on the unified.js ecosystem. It works in several stages:
remark-parse
remark-rehype
rehype-sanitize
This pipeline is extensible - you can add transformations for things like syntax highlighting, math equations, or custom components by inserting additional processing steps. These aren't included yet, but could be added.
Next.js handles the static generation of blog pages using its built-in static site generation features. At build time, the system:
This process happens once at build time, resulting in a collection of pre-rendered HTML files that can be served extremely quickly.
Since we're dependent on GitHub's API which has rate limits, we implement a smart caching system. When content is fetched from GitHub, it's saved to a local cache file. The cache entries include timestamps and expire after a configurable period - typically one hour in development and 24 hours in production.
Before making any API requests, the system checks for a valid cache entry. If found and not expired, it uses the cached content instead of making an API request. If the cache is missing or expired, it fetches fresh content from GitHub and updates the cache.
This caching system is particularly useful during development when you're making frequent changes and rebuilding often, as it prevents you from hitting GitHub's API rate limits.
Setting up your own blog is simple:
# Quick start using bun (https://bun.sh/docs/installation)
git clone https://github.com/Dicklesworthstone/nextjs-github-markdown-blog)
cd nextjs-github-markdown-blog
bun install
# Edit .env.local with your GitHub details, repo url, domain of your blog, etc.
bun run build
bun run start
WordPress is powerful but comes with overhead:
This system replaces all that with a few static files and Git operations you're already doing. And even non-technical people can use the visual markdown preview tools in GitHub, and can have LLMs like ChatGPT take a plaintext post and turn it into markdown; the YAML frontmatter can then simply be compied and tweaked from a previous blog post.
Medium is polished but limiting:
This approach gives you full control while maintaining simplicity.
Building a custom blog often means:
This system leverages GitHub's infrastructure for all of that.
While excellent tools, traditional SSGs:
This approach integrates naturally with Next.js applications while maintaining the benefits of static generation.
The system is pretty fast because:
The system automatically generates rich metadata for every blog post. When a post is rendered, it extracts key information like the title, description, and cover image from the frontmatter. This data is then used to create comprehensive metadata tags including OpenGraph data for social media sharing. All of this happens at build time, so search engines and social media platforms get perfectly optimized content without any runtime overhead.
I'm thinking about adding more features, but want to keep the emphasis on simplicity. Here are some things I would add if I can figure out a way to keep it slick and simple:
By leveraging GitHub as a CMS and combining it with Next.js, we've created a blogging platform that's:
This post itself is written in Markdown (you can see the actual markdown itself here— how is that for meta?) and served through the system, demonstrating its capabilities. I believe this approach to blogging hits a sweet spot for developers and technical teams. It's simple enough to start using immediately but powerful enough to grow with your needs.
The complete source is available on GitHub. Feel free to use it, modify it, or suggest improvements.
Software Engineer and Founder of YouTube Transcript Optimizer
A look at our sleek and efficient new blogging system built with Next.js and Tailwind CSS, designed for fast, responsive, and visually appealing content delivery.
An in-depth look at the technical challenges and solutions in creating the FastAPI backend for YouTubeTranscriptOptimizer.com, a powerful tool for transforming YouTube content into polished written documents and interactive quizzes.