My Markdown Preview Utility

I write a lot of markdown and I am delivered a lot of markdown, increasingly from LLM agents. And while markdown is easy to read inline, often I want a preview of how it will render or maybe I just need to take a nice screenshot for a presentation to the CTO later or maybe I just want to read the long LLM output in a proportional font.

So I’ve been increasingly using the command line script (stored in bin/preview-md) that I wrote a while ago:

#!/bin/bash

if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
    echo "preview-md - Preview markdown as HTML in your browser"
    echo ""
    echo "Usage: preview-md [FILE]"
    echo "       preview-md \"STRING\""
    echo "       command | preview-md"
    echo "       preview-md < file.md"
    echo ""
    echo "Reads markdown from stdin, a file, or a string argument and opens it as HTML in your browser."
    echo "Uses pandoc to convert GitHub Flavored Markdown to HTML."
    exit 0
fi

if ! command -v pandoc &> /dev/null; then
    echo "Error: pandoc is not installed." >&2
    echo "Please install pandoc to use this tool:" >&2
    echo "  macOS: brew install pandoc" >&2
    echo "  Ubuntu/Debian: sudo apt-get install pandoc" >&2
    echo "  Other: https://pandoc.org/installing.html" >&2
    exit 1
fi

timestamp=$(date +%Y%m%d_%H%M%S)
tmpfile="/tmp/preview-md-${timestamp}-$$.html"

# Add cleanup trap to remove tmpfile after browser opens
trap "sleep 5; rm -f '$tmpfile'" EXIT

echo '<link href="https://www.joshbeckman.org/assets/css/site.css" rel="stylesheet"><style>body { max-width: 800px; margin: 1em auto; padding: 1em; font-family: "IBM Plex Sans", sans-serif; }</style>' > "$tmpfile"

if [[ $# -eq 1 ]]; then
    if [[ -f "$1" ]]; then
        pandoc -f gfm -t html "$1" >> "$tmpfile"
    else
        echo "$1" | pandoc -f gfm -t html >> "$tmpfile"
    fi
else
    pandoc -f gfm -t html >> "$tmpfile"
fi

open "$tmpfile"

This little script allows you to pipe a file or string output or just give it a file path and it will use pandoc to render an HTML page and open it in your browser for you. Beautifully flexible.

I have it rendering the HTML with my own site’s CSS because I like that style and specifically those fonts, but you can remove that by simplifying this line:

echo '<style>body { max-width: 800px; margin: 1em auto; padding: 1em; font-family: sans-serif; }</style>' > "$tmpfile"

In Vim

Since this script is so flexible, I’ve hooked it up in a few key places. For one, I have a (Neo)vim command to open the current buffer as a markdown preview:

command! PreviewMd call PreviewMarkdown()

function! PreviewMarkdown()
    let filepath = expand("%:p")
    execute "!preview-md " . shellescape(filepath)
endfunction

In Claude

And I also have made a Claude slash command to have the LLM agent render its last message to me as a markdown preview in the browser:

---
description: Preview Last Response as Markdown
allowed-tools: Bash(preview-md:*)
---

Please take your last response and pass it directly to the `preview-md` command as a string argument for visual preview.

Do this by:
1. Taking the full content of your previous response
2. Running the command: `preview-md "YOUR_LAST_RESPONSE_AS_MARKDOWN"`

Make sure to:
- Escape any double quotes in the content with \"
- Preserve all markdown formatting (headers, lists, code blocks, links, etc.)
- Include any code blocks with proper language identifiers
- Keep all line breaks and whitespace

Just run the command directly without explanation.

So I can now run /preview-md in any Claude conversation and view the LLM’s response nicely formatted.

Example rendered LLM response


Keyboard Shortcuts

Key Action
o Source
e Edit
i Insight
r Random
h Home
s or / Search
www.joshbeckman.org/blog/practicing/my-markdown-preview-utility