Pandoc Instant Markdown-to-HTML

If you write HTML by hand, you already know the pain of typing <ul><li> over and over. Here’s a trick that lets you write plain Markdown inline and convert it on the spot using pandoc as a Vim filter.

The Basic Idea

Vim’s ! command pipes a range of lines through an external program and replaces them with the output. Pandoc reads Markdown on stdin and writes HTML to stdout — a perfect match.

Three Useful Incantations

Original file:

Here is a thing.
- one
- two
Something else.

Convert just a few lines (current line plus the next):

:.,.+1!pandoc

Useful when you only want to convert a list or a short block without touching the surrounding content.

Output:

Here is a thing.
<ul>
<li>one</li>
<li>two</li>
</ul>
Something else.

Convert from the cursor to end of file:

:.,$!pandoc

Wraps everything in proper block-level tags (<p>, <ul>, etc.) but leaves out the <html> boilerplate — great for partials or template fragments.

Output:

<p>Here is a thing.</p>
<ul>
<li>one</li>
<li>two</li>
</ul>
<p>Something else.</p>

Generate a complete standalone HTML document:

:.,$!pandoc -s

The -s flag produces a full page with <!DOCTYPE html>, <head>, default pandoc CSS, and <body>. Handy when you want a self-contained file you can open straight in a browser.

Output:

some.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="pandoc" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <title>-</title>
  <style>
    /* Default styles provided by pandoc.
    ** See https://pandoc.org/MANUAL.html#variables-for-html for config info.
    */
    html {
      color:
#1a1a1a;
      background-color:
#fdfdfd;
    }
    body {
      margin: 0 auto;
      max-width: 36em;
      padding-left: 50px;
      padding-right: 50px;
      padding-top: 50px;
      padding-bottom: 50px;
      hyphens: auto;
      overflow-wrap: break-word;
      text-rendering: optimizeLegibility;
      font-kerning: normal;
    }
    @media (max-width: 600px) {
      body {
        font-size: 0.9em;
        padding: 12px;
      }
      h1 {
        font-size: 1.8em;
      }
    }
    @media print {
      html {
        background-color: white;
      }
      body {
        background-color: transparent;
        color: black;
        font-size: 12pt;
      }
      p, h2, h3 {
        orphans: 3;
        widows: 3;
      }
      h2, h3, h4 {
        page-break-after: avoid;
      }
    }
    p {
      margin: 1em 0;
    }
    a {
      color:
#1a1a1a;
    }
    a:visited {
      color:
#1a1a1a;
    }
    img {
      max-width: 100%;
    }
    svg {
      height: auto;
      max-width: 100%;
    }
    h1, h2, h3, h4, h5, h6 {
      margin-top: 1.4em;
    }
    h5, h6 {
      font-size: 1em;
      font-style: italic;
    }
    h6 {
      font-weight: normal;
    }
    ol, ul {
      padding-left: 1.7em;
      margin-top: 1em;
    }
    li > ol, li > ul {
      margin-top: 0;
    }
    blockquote {
      margin: 1em 0 1em 1.7em;
      padding-left: 1em;
      border-left: 2px solid
#e6e6e6;
      color:
#606060;
    }
    code {
      font-family: Menlo, Monaco, Consolas, 'Lucida Console', monospace;
      font-size: 85%;
      margin: 0;
      hyphens: manual;
    }
    pre {
      margin: 1em 0;
      overflow: auto;
    }
    pre code {
      padding: 0;
      overflow: visible;
      overflow-wrap: normal;
    }
    .sourceCode {
     background-color: transparent;
     overflow: visible;
    }
    hr {
      border: none;
      border-top: 1px solid
#1a1a1a;
      height: 1px;
      margin: 1em 0;
    }
    table {
      margin: 1em 0;
      border-collapse: collapse;
      width: 100%;
      overflow-x: auto;
      display: block;
      font-variant-numeric: lining-nums tabular-nums;
    }
    table caption {
      margin-bottom: 0.75em;
    }
    tbody {
      margin-top: 0.5em;
      border-top: 1px solid
#1a1a1a;
      border-bottom: 1px solid
#1a1a1a;
    }
    th {
      border-top: 1px solid
#1a1a1a;
      padding: 0.25em 0.5em 0.25em 0.5em;
    }
    td {
      padding: 0.125em 0.5em 0.25em 0.5em;
    }
    header {
      margin-bottom: 4em;
      text-align: center;
    }
    #TOC li {
      list-style: none;
    }
    #TOC ul {
      padding-left: 1.3em;
    }
    #TOC > ul {
      padding-left: 0;
    }
    #TOC a:not(:hover) {
      text-decoration: none;
    }
    code{white-space: pre-wrap;}
    span.smallcaps{font-variant: small-caps;}
    div.columns{display: flex; gap: min(4vw, 1.5em);}
    div.column{flex: auto; overflow-x: auto;}
    div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
    /* The extra [class] is a hack that increases specificity enough to
       override a similar rule in reveal.js */
    ul.task-list[class]{list-style: none;}
    ul.task-list li input[type="checkbox"] {
      font-size: inherit;
      width: 0.8em;
      margin: 0 0.8em 0.2em -1.6em;
      vertical-align: middle;
    }
    .display.math{display: block; text-align: center; margin: 0.5rem auto;}
  </style>
</head>
<body>
<p>Here is a thing.</p>
<ul>
<li>one</li>
<li>two</li>
</ul>
<p>Something else.</p>
</body>
</html>

Why This Works So Well

The filter approach means you never leave your editor. Write a quick Markdown list, visually select it, run the filter, and the raw HTML is sitting right there — no browser tab, no separate tool, no copy-paste.

Pandoc supports a huge range of input and output formats, so the same trick works for converting to Markdown, reStructuredText, or even LaTeX if the mood strikes.

my DevOps Odyssey

“Σα βγεις στον πηγαιμό για την Ιθάκη, να εύχεσαι να ‘ναι μακρύς ο δρόμος, γεμάτος περιπέτειες, γεμάτος γνώσεις.” - Kavafis’ Ithaka.



Pandoc as a Vim Filter: Instant Markdown-to-HTML in Your Editor

2026-03-11

Series:lab

Categories:general

Tags:#pandoc, #markdown, #html, #vim, #lab


Pandoc Instant Markdown-to-HTML: