Markdown to HTML Converter

A simple Markdown to HTML converter built in Go

All projects  ·  About Matt

Try it!



Visit the GitHub Repo

Description


Context: Writing raw HTML for documentation is tedious. I wanted a CLI tool that lets me write Markdown and get clean HTML pages out — and I wanted to build it in Go to learn the language's standard library.

Challenges: Parsing Markdown correctly across headings, code blocks, inline code, links, and nested lists without pulling in an external parsing library. The main challenge was handling edge cases in the line-by-line parsing logic without a formal grammar.

Result: A working CLI tool that converts Markdown files into styled HTML — handles all standard elements including headings, code blocks, lists, and links. Serves as the foundation for generating styled article pages from plain Markdown source.

How the Parser Works


Markdown Parser — Line-by-Line State Machine (Go) ═════════════════════════════════════════════════════ file.md → bufio.Scanner → one line at a time │ ┌─────────────▼─────────────┐ │ PARSER STATE │ │ │ │ ┌─────────────────────┐ │ │ │ NORMAL │ │ │ └──────────┬──────────┘ │ │ │ │ │ line starts with: │ │ │ │ "#" → emit <h1>–<h6> │ │ │ │ "```" → CODE_BLOCK ────┤ │ collect lines │ │ until next "```"│◄── blank line inside? │ emit <pre><code>│ stay in state (fix) │ │ │ "- "/"* " → LIST │ │ emit <li> │ │ track <ul> open │ │ │ │ text → emit <p> │ └────────────────────────────┘ │ ▼ emit HTML tokens │ ▼ ┌─────────────────────────┐ │ output file.html │ │ + injected CSS │ └─────────────────────────┘ The state machine was the key fix — a stateless processor closed code blocks on blank lines, producing malformed HTML.

Inline Transforms — Applied Within Each Line ═══════════════════════════════════════════════ input: "See **bold** and `code` and [link](url)" │ ▼ strings.Replace passes (in order): "**text**" → <strong>text</strong> "*text*" → <em>text</em> "`code`" → <code>code</code> "[t](url)" → <a href="url">t</a> output: "See <strong>bold</strong> and <code>code</code> and <a href="url">link</a>" Applied after block detection decides the wrapper tag. Order of transforms matters — bold before italic prevents ** from being partially consumed.

In Action

CLI proof points

  • Markdown input turns into styled HTML without writing page markup by hand.
  • Go standard-library code keeps the converter small and dependency-light.
  • Stateful parsing handles multi-line blocks instead of treating every line alone.
CLI converting a Markdown file

Generated HTML proof points

  • Block detection chooses the wrapper tag before inline transforms run.
  • Inline transforms cover bold, italic, code, and link syntax in a predictable order.
  • The output can be dropped into static documentation pages.
Generated HTML output

Dev Notes

Problems Solved

Writing raw HTML for documentation by hand was tedious and error-prone. I needed a CLI that accepted Markdown as input and output styled HTML pages — and building it in Go was a chance to learn the standard library on a real task.

Errors & Fixes

The line-by-line parser broke on nested lists and code blocks that contained blank lines — it would close the block early and emit malformed HTML. Fixed by adding a state machine that tracked open block contexts across lines instead of processing each line independently.

What I Learned

Go's bufio.Scanner, string manipulation with the strings package, and CLI flag design with os.Args. More broadly, how stateful line parsing works and why a formal grammar is worth the extra effort on complex inputs.


Back to top