Building Verto: A Modern Blog Engine

Verto Team
designengineeringopen-source

Building Verto: A Modern Blog Engine

Every tool begins as frustration. We had been maintaining a developer blog for two years β€” first on Gatsby, then Hugo, then a brief, regrettable stint with a custom CMS β€” and each time, something was missing. The beautiful reading experience of OpenAI's blog, Mintlify's intelligent structured navigation, and Notion's expressive block system had never existed in a single coherent tool.

So we built Verto β€” named from the Latin vertō, meaning "to transform." The name felt right: you write plain Markdown, and Verto transforms it into something that looks and feels like a premium publication.

What is Verto? Verto is a blog engine that processes Markdown files and renders them with rich block elements, structured navigation, and a magazine-grade reading experience β€” all from a single verto.config.ts file.

The core philosophy is zero compromise: authors should write in plain Markdown with optional syntax extensions. Readers should get a publication-quality experience. Developers should have zero-config deployment.


Architecture Decisions

Choosing the right framework was the hardest part. After a two-week evaluation spike, the architecture settled into three clear layers:

  1. Parser Layer β€” An extended unified/remark pipeline that handles custom syntax like callouts, toggles, and inline comments
  2. Renderer Layer β€” Astro components that convert AST nodes into semantic HTML with zero client-side JavaScript
  3. Delivery Layer β€” Static site output with optional edge runtime for dynamic features

The Parser Pipeline

The remark ecosystem gave us 80% of what we needed for free. The remaining 20% β€” callouts, inline comments, toggle blocks β€” required custom AST transformers. Here's the core transformer for callout syntax:

import { visit } from "unist-util-visit";
import type { Plugin, Root } from "unified";

// Matches: > [!INFO] Optional title
const CALLOUT_RE = /^\[!(INFO|WARN|OK)\]\s*(.*)/i;

export const remarkCallouts: Plugin<[], Root> = () => (tree) => {
  visit(tree, "blockquote", (node, index, parent) => {
    const firstPara = node.children[0];
    if (firstPara?.type !== "paragraph") return;
    const text = toString(firstPara);
    const match = CALLOUT_RE.exec(text);
    if (!match) return;

    // Mutate node into callout type
    node.type = "callout" as any;
    (node as any).calloutType = match[1].toLowerCase();
    (node as any).calloutTitle = match[2];
  });
};

The key insight: rather than building a new parser from scratch, we extended the existing remark ecosystem. This gave us CommonMark compatibility for free while enabling Verto-specific syntax with minimal code.

The Renderer

Astro was a clear winner for this use case. Its island architecture meant we could ship zero JavaScript by default β€” critical for a reading-focused content site β€” while still enabling interactive islands for things like code copy buttons and inline comment popups.

// Post layout β€” Astro component
import CodeBlock from "../components/CodeBlock.astro";
import Callout from "../components/Callout.astro";

const { post } = Astro.props;

// Component mapping lets MDX use custom renderers
const components = {
  pre: CodeBlock,
  blockquote: Callout,
};

"The best blog engine is one your readers never notice β€” they only notice the content." This principle guided every design decision we made, from typography choices to animation timing.


The Block System

Notion proved that a block-based editor doesn't have to feel like a database. Verto takes that philosophy to Markdown: every block type has a clean, unambiguous syntax and a beautiful default rendering.

Callout Blocks

Three callout types cover 95% of author needs:

Use info callouts for contextual notes, tips, and supplementary details that enrich the main narrative without interrupting it.

Warning callouts surface potential pitfalls. Ideal for deprecation notices, breaking changes, and "don't do this" guidance. Always pair a warning with a recommended alternative.

Tip callouts highlight best practices, recommended patterns, and things that are working exactly as intended. Use them to reinforce positive behaviors.

Toggle Sections

Collapsible blocks let authors include supporting detail without cluttering the main reading flow:

Implementation details: How toggle syntax works in Markdown

Toggle blocks use a custom remark plugin that transforms fenced divs into collapsible <details> elements. The syntax is straightforward:

<Toggle title="Your section title">
  Content goes here. Supports full Markdown including
  code blocks, lists, and nested components.
</Toggle>

The plugin preserves all inner content and wraps it in a styled <details> / <summary> pair with smooth open/close animations.

Performance benchmarks: Verto vs. other static site generators

We benchmarked Verto against several popular static site generators using a corpus of 1,000 Markdown posts with mixed content types:

EngineBuild time (1000 posts)Lighthouse score
Verto4.2s100
Astro (default)6.8s98
Next.js19.1s82
Gatsby34.7s91
Hugo1.1s99

Hugo wins on raw build speed, but lacks the component model needed for interactive islands. Verto strikes the best balance between build performance and feature richness.

Task Lists

The Verto v0.1 roadmap, tracked as a task list right in this blog post:

  • Core Markdown parser with remark
  • Callout block syntax and renderer
  • Inline comment system (c- footnote syntax)
  • Dark mode with system preference detection
  • Excalidraw embed integration
  • Full-text search
  • RSS feed generation
  • Comment threads (not inline β€” threaded discussions)

The Inline Comment System

This is Verto's most distinctive feature. Every blogger has thoughts they want to share that don't fit the narrative flow β€” a parenthetical, a confession, a "here's what really happened" aside. Designing the interaction for this took longer than any other part of Verto.

The syntax reuses Markdown's footnote notation with a c- prefix:

Some highlighted text[^c-1] continues normally.

[^c-1]: This is my author's note about this text.
[^1]: This is a regular footnote, rendered at the bottom.

The parser distinguishes between [^c-N] (inline comment β€” shown as popup) and [^N] (footnote β€” rendered at page bottom). This means author commentary and formal citations coexist without conflict.

Footnote vs. Inline Comment β€” Inline comments ([^c-N]) are for author asides, shown on click. Regular footnotes ([^N]) are for citations and references, shown at the bottom. The difference matters for reading flow.


Rich Media Embeds

Static blogs have historically been terrible at embedding rich content. Verto treats embeds as first-class citizens with a consistent visual language.

Images and Captions

Every image in Verto can carry a caption, rendered in a consistent <Figure> component:

Verto's three-layer architecture: Parser, Renderer, and Delivery
Fig. 1 β€” Verto's three-layer architecture. Markdown enters the parser, flows through AST transformation, and exits as static HTML.

Diagrams

For architectural diagrams and flowcharts, Verto supports placeholder blocks that can be replaced with Excalidraw or Mermaid embeds in production:

Content Pipeline

Architecture overview: Markdown source β†’ Remark AST β†’ Custom transformers (callouts, comments, toggles) β†’ Astro renderer β†’ Static HTML output

External references render as rich preview cards instead of plain hyperlinks:

tsaiggo/verto β€” GitHubA modern blog engine β€” Write. Transform. Publish. Fusing Mintlify navigation, Notion blocks, and OpenAI aesthetics.github.com Astro β€” The web framework for content-driven websitesAstro builds fast content sites, powerful web applications, dynamic server APIs, and everything in-between.astro.build

The Design System

Verto's visual language is intentionally restrained. The goal: the design should disappear, leaving only the content. Here's the complete typography and spacing spec:

ElementSizeWeightLine Height
H1 (post title)clamp(28px, 4vw, 42px)7001.2
H2 (section)26px7001.3
H3 (subsection)20px6001.4
Body18px4001.78
Caption / meta13px4001.5
Code13.5px4001.7

Both light and dark modes are defined as CSS custom properties on :root and html.dark respectively, enabling instant, flicker-free theme switching. The color palette uses a minimal set of semantic variables β€” --bg, --text, --accent-blue, --border β€” that adapt automatically.


What's Next

Verto v0.1 ships with the core feature set. The roadmap for v0.2 focuses on collaboration. That means:

  • Real-time collaborative editing via Y.js CRDTs
  • Version history with visual diffs
  • Threaded comment discussions (separate from inline comments)
  • AI writing assistant (optional, privacy-respecting)

React Server Components1 make this possible β€” the server can handle collaborative state while the client remains lightweight, only hydrating the interactive editing islands.

Try Verto today. The source code is open. Star the repo, file issues, and join the conversation. The best version of Verto will be shaped by the community that uses it.


Footnotes

  1. React Server Components were introduced in React 18 and became stable in React 19, enabling server-only rendering of components without client-side JavaScript bundles. ↩