Markdown comments: hide notes in a .md file
Bobby Huang ·
Markdown has no comment syntax of its own, but two tricks work. The first is an HTML comment, written <!-- your note -->. CommonMark passes it into the output as raw HTML, and browsers normally keep comments off the screen. Trick two is a link reference definition that no link uses, written [//]: # (your note). CommonMark treats that line as a definition and writes nothing for it. Each trick has limits. An HTML comment that starts its own line can run across blank lines. A viewer that escapes HTML may print it as text. The [//]: # line must sit on its own line, cannot follow paragraph text directly, and its note cannot hold a blank line. Neither trick is private. Anyone who opens the raw file can read every note.
Key takeaways
- CommonMark has no comment rule. HTML comments and unused link reference definitions fill the gap.
- A comment that starts its own line can span blank lines, but any text after --> on its closing line stays raw and is not read as Markdown.
- The [//]: # (note) line must start its own line, can't follow a paragraph line directly, and leaves nothing in the HTML output.
- Inside a code span or any code block, either trick shows as plain text.
- Comments are not secret. They remain part of the raw file, and HTML comments also land in the page's HTML.
You want to leave a note in a .md file: a reminder to check a figure, a question for a teammate, or a line that tells an agent which section to leave alone. You want it in the file, but not on the page.
Markdown has no comment syntax, so people borrow two tricks. For the full rules on tags, read raw HTML in Markdown.
Markdown has no comment syntax
CommonMark defines no comment syntax. What it does have are two features that happen to leave no visible text behind:
- HTML comments. CommonMark passes raw HTML through to the output, and comments are a kind of HTML.
- Link reference definitions. A line like
[label]: urldefines a link target. It produces no output on its own, so a definition that no link uses vanishes.
Both follow from the CommonMark spec. They fail in different ways.
Trick 1: an HTML comment
The launch date is May 4.
<!-- TODO: Jane Doe to confirm the date with the venue -->
Tickets go on sale a week before.
Run that through pandoc -f commonmark -t html and you get two paragraphs, with the comment between them as raw HTML. Browsers normally keep comments off the screen, so the reader sees only the two sentences.
A comment can also sit inside a sentence. Send it Friday <!-- or Monday? --> after review. stays one paragraph, and the comment passes through in the middle of it.
When a line starts with <!-- (indented three spaces or fewer), the comment becomes an HTML block. That block can span many lines, blank ones included. The spec ends it at the first line that contains -->, so nothing inside is read as Markdown until then:
<!--
Open questions for Doe Holdings:
- Which region goes first?
- Who signs off on the final draft?
-->
The list inside never renders. It all passes through as one comment.
What breaks it:
- Text after
-->on the same line. When<!-- note -->*draft*starts its own line, the*draft*belongs to the comment block. It is passed through raw, not turned into italics, and a browser will likely show the asterisks. Put the closing-->at the end of its line. - Nesting. A comment ends at the first
-->. In<!-- outer <!-- inner --> rest -->, the words after the first-->are no longer inside a comment, so a browser may show them. - Starting with
>. The spec counts<!-->as a complete, empty comment. Write<!-->note-->and "note" plus the closing arrows can end up as visible text. Leave a space after<!--. - Four spaces of indentation. At the top level, after a blank line, a comment line indented four spaces becomes an indented code block, and the comment shows as code. Up to three spaces is fine. Inside a list item the count starts from the item's text, so the same indent may not make code there.
One handy side use: the pandoc manual suggests a comment such as <!-- end of list --> to split one list into two, since the comment gives no visible output. The post on nesting lists and quotes has more list rules.
Trick 2: the [//]: # line
Tickets go on sale a week before.
[//]: # (Jane Doe: check the refund policy before this goes out)
Refunds close 48 hours before the event.
This is a link reference definition. The label is //, the destination is #, and the part in parentheses is the link title. CommonMark lets a title sit in double quotes, single quotes or parentheses, so [//]: # "note" works too. Since no link in the file uses the label //, the line writes nothing to the output. Pandoc gives two paragraphs and no trace of the note, not even in the HTML.
Other labels work too, and you will also see [comment]: # and [comment]: <> (note). The empty <> is a valid destination. A label needs at least one character that isn't a space, and it can't hold unescaped square brackets, so a short fixed label like // is the safe pick.
What breaks it:
- Following a paragraph line directly. A definition can't interrupt a paragraph. Put
[//]: # (note)directly below a sentence and it joins that paragraph as visible text. Add an empty line above it. A heading is fine right above it. - Using it inside a sentence. The trick only works when the line starts with the label.
Send it [//]: # (note) Friday.prints exactly as typed. - Text after the title.
[//]: # (note) more wordsis not a definition, because only spaces or tabs may follow the title on that line. The whole line shows. - An empty line inside the note. A title can wrap across lines, but it can't contain a blank line. Split the note at a blank line and both halves show up.
- Parentheses inside the note. Inside a parenthesis title, a
(or)has to be escaped with a backslash.(call (or email) Jane)breaks the line and it shows on the page. Write\(or email\), or switch to double quotes. - A label your text uses. A definition turns matching bracket text into a link anywhere in the file. With
[comment]: #defined, a later "see the [comment] below" becomes a link to#.[//]rarely appears in ordinary prose, which makes a clash less likely.
Definitions work inside lists and block quotes too. Like comments, they turn into plain code text inside a code fence, or when indented four spaces at the top level after a blank line.
Which one to use
- Short note between paragraphs: either works.
[//]: #leaves nothing in the output. - Note inside a sentence: only an HTML comment works.
- Long note with blank lines or a list: only an HTML comment that starts its own line works.
- Note that should not reach the HTML at all: use
[//]: #, or run pandoc with--strip-commentson files that use HTML comments.
Where comments still show up
A hidden note is only hidden on some pages, and never in the file itself.
- The raw file. Both tricks are plain text in the
.mdfile. Anyone who opens the source, diffs it, or reads it in a terminal sees every note. - The page source. An HTML comment is copied into the HTML. Anyone who views the source of the rendered page can read it.
- Viewers that escape HTML. The spec doesn't say what a viewer must do with raw HTML. Some print tags as visible text, and a viewer like that may show your comment on the page.
- Code. Inside a code span, a fenced block or a block indented as code, both tricks are literal text.
Treat every comment as public. Never put a password, an API key, a client name or anything you'd regret someone reading in a Markdown comment.
Agents sometimes leave notes in comments, and rendered pages may hide them. When to check the raw text is covered in the guide to rendered and raw agent Markdown.
Add a comment, step by step
- Pick the trick. Use
<!-- -->for notes inside sentences, or for long notes with blank lines, starting the comment on its own line. Use[//]: # (note)for one-line notes between blocks. - Give it room. Give a
[//]: #line an empty line above it. End an HTML comment with-->as the last thing on its line. - Keep the indent under four spaces. Start the line at the left margin to be safe.
- Check the note's own text. Escape parentheses in a
[//]: #title, and keep-->out of an HTML comment. - Test the render. Run
pandoc -f commonmark -t html notes.mdand check that no note turns up inside a paragraph, or open the file in the app your readers will open it in. - Search before you share. Run
grep -nE '<!--|^\[//\]' notes.mdas a starting search. It may miss notes that are indented or use another label, so read the whole file before sharing and delete any note that shouldn't travel.
If you tell an agent how to mark its notes, put that rule in its instruction file so it uses the same trick each time. For the core elements that sit around these tricks, check the Markdown syntax guide.
Where Sashmark fits
Sashmark, a desktop app still in the works, aims at reading and editing files that agents keep rewriting. It is in development and has not been released. The app itself has no AI features. The v1 plan has the live render cover headings, lists, tables, code blocks and block quotes, and raw HTML is planned to remain inert. To follow progress, read the roadmap, browse the help pages or scan the planned features.
More about the app lives at sashmark.com.
Questions
- Can a Markdown comment span multiple lines?
- An HTML comment can, if it starts its own line. The block runs from the line that starts with <!-- (indented three spaces or fewer) to the first line that contains -->, blank lines included. A [//]: # note can wrap onto a second line inside its parentheses, but an empty line inside the note breaks it, and the text then shows on the page.
- Does pandoc keep my comments when it converts a file?
- In HTML output, pandoc passes HTML comments through as raw HTML, so they sit in the page source. Its --strip-comments option removes them instead. A [//]: # line is a link reference definition, so it produces no output at all.
- Should I leave notes for an AI agent in comments?
- It works for short notes about the file, like a reminder to recheck a date. Keep in mind that a reader who only looks at the rendered page may miss them, and anyone with the raw file will see them. Never put passwords, keys or private details in a comment.