I recently decided to start blogging, so I built my own platform and thought it’d be nice to share how I made it.
Introduction
Me
Since this is the first blog, here’s a bit about me. I’m Quang, a full-stack software engineer. I also do design. My main focus is on design engineering, Typescript (mostly through React), Python, performance tuning, and a bit of competitive programming.
The Blog
This blog will be used to showcase the work I’m most proud of as well as the main documentation page for my interesting mini projects.
Why not use an existing platform
There are thousands of blogging platforms, but most are closed boxes: I can’t freely add the features I want (things mentioned below), style elements exactly how I like, or configure the layout behaviors. Writing my own blogging system would be a great chance for me to study a CMS app structure, and customize everything in the way I prefer.
As you might have already seen from the landing page of this blog, that's exactly how I prefer the layout to be, and it's uncommon for blog sites to have such layout. In additional, there are a lot more Markdown features that I support:
Syntax highlighting
Code blocks can show a header, support highlighting for common languages.
Syntax highlighting isn’t new, but this one also has a nicer font and the italic keyword support themes (Inspired by Josh Comeau). Even top-tier blogs sometimes struggle with light/dark code blocks as the library often has to render the theme client-side, breaking the hydration.
Callouts (multiple variants)
Markdown doesn’t support callouts natively. Inspired by Docusaurus's Admonitions, I built my own variants:
A normal callout with a moderately important note.
A warning block for side effects, environment caveats, or anything that needs careful attention.
Critical errors or important caveats that must stand out.
A satisfying success block, pleasant to look at, even when it’s just for emphasis.
In advanced, I also support colored inline text for important notes, including custom colors.
Mermaid diagrams support
Integrating mermaid.js was quite challenging, especially with Node.js and light/dark mode support, which is why most systems skip it. I support it because I also want to use it for all of the diagrams I make on this site, and there are no better alternatives.
Loading Mermaid...
I've also done some premature styling for the nodes and wrote custom functions to have nicer arrow icons. This makes the diagrams more consistent with the current theme.
Math support
At some point, some of my blogs would probably need to render Math equations, so it's nice to support a simple set of Latex syntaxes by default, the platform allows both inline math: x=y+3 and display blocks:
⎩⎨⎧3x+5y+z7x−2y+4z−6x+3y+2z=3=4=2
Image carousel
Image carousels let users group related photos into one component.
Upcoming features
I’ll keep adding pragmatic features and shipping small improvements. Planned items include:
Lightweight full-text search engine
Basic page analytics
A better content versioning system
These will be discussed in more recent blogs as my need to manage this platform grows.
Tech stack
The stack behind this site includes:
It’s a Next.js (App Router) app with Chakra UI as the main UI library. I chose Chakra over newer options like shadcn/ui because its theming system is mature and fits this use case better, I need something quick and fast out of the box, with different themes that I can switch simply in a few lines of code. Chakra v3 has very good support for this, it also has a very competent set of components I can use. I still use shadcn though, it's just that for this project, a ready-to-use library should be better.
The application is intentionally generic and config-driven: content and design are fully customizable for different use cases, not just my own. As a result, everything in this site is just at the app level settings and there's no footprint of me or my blog in the codebase. This is also one of the ways for me to keep the architecture and the code clean enough.
Authorization
Apart a basic RBAC model, I manage sessions with iron-session. The flows are straightforward CRUD. The project doesn’t expose traditional REST endpoints; instead it relies on Next.js's server actions and server components. They retrieve sessions slightly differently, but the integration is similarly simple.
A lightweight middleware checks cookies for early rejection, and Redis is also added for fast cache/session validation after that first check.
Although the architecture supports multiple users, but features are intentionally scoped for a single-author blog mostly for now.
Content Organization
I use Kysely as a type-safe SQL query builder. It’s lightweight to start with, and it also powers my TypeScript migration scripts, which have worked well so far.
All blog content lives in the database rather than the repo. This allows more flexible caching, filtering, categorization, and consistent behavior at scale for post/blog queries. But that also means I have to build my own version control system, which I don't have yet.
The content is in markdown with custom support for my own features. It's closed to MDX, but not quite there. MDX is great for rich interactivity, but its file-based workflow doesn’t fit my current goals, my content isn't stored as files but database records, so it's not easy to reference components inside; a database-backed MDX editor might come later once I figure out how to store safe and organized MDX content.
Remark and Rehype custom plugins
To support the additional Markdown features above, I built a few custom Remark and Rehype plugins.
Loading Mermaid...
The unified rendering pipeline (which parses Markdown into MDAST and HAST) is extended with:
remarkCalloutPlugin: Detects inline and block callout syntax (e.g., starts with :::callout/:::warning, etc and ends with :::) and converts it into a dedicated callout node.
rehypeMathPlugin: Used along with remarkMath to convert code tags with language-math class into math and inlineMath tags that allows me to render via KaTex separately.
rehypeInlineHighlighterPlugin: Converts links whose href starts with color:: into coloredSpan nodes.
I also provide a component map that swaps most supported HTML tags for Chakra UI components where appropriate.
Design
The site aims for a calm and simple aesthetic with a custom theme called Vegetable. The colors are based on natural tones and actual vegetables to make the interface look clean and comfortable to read.
I decided not to follow newer design trends with this website. Instead, this design would be something that looks consistent and won’t feel outdated for a while, even if it appeals to fewer people, at least, this is what I think should feel more tasteful than most UI systems nowadays. The admin dashboard, however, uses a more standard Chakra UI theme for easier management. I will, however, in the future, write a lot of short blogs about the effects and components that I find interesting to remake.
Once the project is fully polished, I plan to open-source it. The idea is to keep the system stable while updating the theme once in a while with different color palette options or layout variations.
Overall, this project has been both a technical challenge and a way for me to organize what I’ve learned. It really marks a small starting point for documenting my work and ideas, I'll add more updates as I go.