utilime

Small tools that finish the job.

Turn Markdown into HTML

Paste Markdown, read the HTML. The parser is a few kilobytes of this page rather than a library, and it names what it found so you can check the parse agreed with you.

Drop a file hereor click to choose · it stays on your device

How it works

Markdown becomes HTML in two passes, and knowing which pass you are arguing with explains most surprises. The first reads the document by lines and settles structure: this run of lines is a paragraph, that one a list item, these three a fenced code block. Only then does the second pass look inside each piece of text for the emphasis, links and code spans living there. A stray blank line changes the first pass, and a missing blank line is the reason a list sometimes renders as one long sentence.

Which Markdown this page speaks

The base is CommonMark 0.31.2: headings in both styles, fenced and indented code, block quotes with lazy continuation, ordered and bullet lists at any nesting depth, thematic breaks, emphasis by the delimiter-run rules, links inline and by reference, images, autolinks, backslash escapes, and hard and soft line breaks. Tables, task boxes and strikethrough appear nowhere in that document. They come from GitHub, so they sit behind a switch that starts on, because a pipe table is the commonest thing people paste that plain CommonMark declines to draw.

Two omissions are worth stating rather than leaving you to discover. Named character references are decoded for the several dozen anybody actually types, so the one for a no-break space produces a real no-break space, while a rarity outside that set stays on screen exactly as written instead of quietly vanishing. Footnotes, definition lists and mathematics are absent, and each of those belongs to a further dialect rather than to Markdown.

One newline, two right answers

The line-break rule is what trips people most. CommonMark asks for two trailing spaces, or a trailing backslash, before it emits a break element inside a paragraph. A lone newline is a soft break: the HTML gets a newline and the browser draws a space. GitHub's own published grammar agrees exactly, so a readme file with single newlines renders as one flowing paragraph on the repository page.

The comment boxes on that same site disagree. Issues, pull requests and discussions turn every single newline into a break element, because a bug report typed in a hurry reads wrongly otherwise. The same characters therefore produce two different documents depending on which field received them, and neither renderer is broken. The switch here picks between the two, and the starting text carries a backslash break so the effect of the choice shows immediately.

Raw HTML is the decision with consequences

CommonMark permits raw HTML in the source and passes it through untouched. Correct for a document you wrote yourself; wrong as a converter's default, because a converter's output usually ends up on a page while its input usually arrived by paste. A script tag in the Markdown becomes a script tag in the HTML, and whoever inserts that HTML has published a stranger's code. So the default escapes it: the angle brackets become entities and the tag appears as visible text.

Link destinations are the same problem wearing a different coat. Square brackets, round brackets, and a scheme that runs code rather than fetching a document is a valid link that reference implementations emit unchanged, because filtering is defined as the job of whatever embeds them. In the escaping mode this page is that embedder, so an executable scheme is dropped from the attribute and reported in a row of its own. Base64 image data is allowed through for images, since a picture cannot execute. The vector format is excluded from that pass, being a document, and a document can carry script.

The passthrough mode does none of this, which is the entire meaning of choosing it. Pick it when the Markdown is yours, the tags in it are yours, and you want the parser to leave them where they are.

Why the preview is text

Nothing on this page inserts your HTML as HTML. Every row of a result is written to the document as text, so the panel showing converted output shows source rather than a rendering, and the second panel resolves the markup into plain text instead: a heading gets underlined, a list gets bullets, a code block gets indented. A pasted script tag is inert here in either mode, which was checked in a browser rather than reasoned about.

That is a deliberate limit rather than a missing feature. A live preview of arbitrary pasted HTML needs a sandboxed frame with scripting switched off, and a sandbox is one attribute away from being no sandbox at all. Download the file and open it locally for a real rendering, in a page whose only content is yours.

What the counts mean

The four figures beside the output are counted from the parse rather than from the characters, the only way they can be right. Headings counts heading elements produced, so a hash inside a fenced code block is not one and a line underlined with equals signs is. Links counts anchor elements, which takes in autolinks and reference links while leaving out images. Code blocks counts preformatted elements, with inline spans named separately underneath. The last figure counts the text panel, so markup characters and link destinations are excluded while text inside a visible code block is not.

Questions

Why is my list rendering as one paragraph?

Almost always a missing blank line above it. A list starting immediately after a line of ordinary text reads as a continuation of that paragraph, because in CommonMark only a bullet or the number one may interrupt a paragraph at all. Put an empty line between the sentence and the first item. The reverse case exists too: an unwanted blank line between items makes the list loose, every item then gets wrapped in a paragraph, and vertical space appears.

Why did my single newlines disappear?

CommonMark treats them as soft breaks, and a soft break renders as a space. Two trailing spaces or a trailing backslash make a real break element. If the text came from a GitHub comment box, where a lone newline does break the line, switch the line-break option to the GitHub setting and the paste renders the way it did there.

Is the output safe to put straight onto a page?

In the escaping mode it carries no tags from your input and no executable link destinations, which removes the usual routes. It is still not a sanitiser: a sanitiser inspects HTML that already exists, against a policy, and this is a converter. For anything untrusted, run the output through a sanitiser you configured, on the machine that will serve it. In the passthrough mode the output can contain whatever the input contained, deliberately.

Why does my link target look different from what I typed?

Destinations are percent-encoded the way reference implementations do it: a space becomes %20 and an accented letter becomes two bytes. Ampersands become entities so the attribute stays well formed. That is notation rather than a change of value, and the encoded form is what belongs in an attribute.

Does anything inside a fenced code block get parsed?

No, and that is the point of a fence. Everything between the markers is content: asterisks stay asterisks, a hash stays a hash, and a nested run of backticks needs an outer fence one backtick longer than the inner one. The word after the opening fence becomes a language class on the code element, which is what syntax highlighters read.

Can it go the other way, from HTML back to Markdown?

Not here. That direction is a harder problem with no single answer, because much HTML has no Markdown spelling at all: a table with merged cells, a division with a class, an inline style. Every converter attempting it has to decide what to discard, and the decisions differ enough that two of them hand you two different documents.