Markdown Code Blocks with File Names and Line Numbers: A Practical Convention
Quick answer
There's no official Markdown syntax for labeling a code block with a filename or line numbers. CommonMark's spec defines the fence's info string for one thing, really — a language identifier, placed right after the opening backticks — and says plainly that it "does not mandate any particular treatment" of anything beyond that. GitHub's own docs stop at the same point: name the language, get syntax highlighting, nothing more.
That gap is exactly why every tool that shows a filename or a line number above a code block invented its own way to write it. Ever wonder why a snippet copied from one site breaks when pasted into another editor? This is usually why. Below is the informal convention most renderers converge on, where it comes from, and the fallback that works everywhere — including in a fenced code block with syntax highlighting.
Step-by-step: labeling a file and its line numbers in a code block
Start with the language identifier
Every renderer that supports syntax highlighting reads the first word of the info string as a language name. Three backticks followed by ts, python, or bash is the one piece of this convention that's actually portable:
function greet(name: string) {
return `Hello, ${name}`;
}Get this part right first. Everything else layers on top of it, and a wrong or missing language name means no highlighting at all, no matter what else you add.
Add the filename after the language
The most common informal pattern places the file path right after the language identifier, sometimes with a colon-separated starting line number:
// app.ts:12
function greet(name: string) {That colon notation isn't arbitrary. It's the same shorthand you'll recognize from compiler errors and linter output (app.ts:12: error ...), which is probably why it became the default choice once people started extending fenced code blocks informally. Whether a snippet needs this treatment at all — versus a plain inline code span — is worth deciding first. A one-line file reference rarely needs a fenced block with a filename annotation.
When you need a line range or highlight
Some renderers support a brace-delimited range to highlight specific lines, like {2-4} appended after the filename. It's a useful convention where it's supported — static site generators and documentation themes are the most common home for it — but it isn't part of any Markdown spec, and a renderer that doesn't recognize it will usually just print the braces as literal text in the output.
Test in your actual renderer before relying on it
None of this is standardized, so the only reliable check is to render the block in the exact tool your reader will see it in. A convention that works on GitHub may not survive a static site generator, and a documentation theme's filename syntax may not mean anything to a plain Markdown viewer.
Common problems and fixes
"My renderer shows the filename as literal text in the output"
This is the most common failure, and it means the renderer doesn't parse anything past the language identifier — which, per the CommonMark spec, it's fully entitled not to. The fix is to stop relying on info-string syntax and put the filename in plain prose or a bold line directly above the fence instead:
app/controllers/todo.js
module.exports = { create, update, destroy };This renders identically everywhere, which matters more than saving one line when you're writing documentation meant to be read outside a single tool. The same discipline applies across a document — see writing technical docs in Markdown for more on keeping conventions consistent file to file.
"Line numbers don't show up at all"
Line numbering is a rendering decision, not something you write into the Markdown source. Highlighter libraries like Prism.js and highlight.js commonly ship a line-numbers option that a renderer turns on independently of anything in the fence's info string. So if your published page isn't showing line numbers, the fix is almost always a renderer or theme setting, not a Markdown syntax change.
"Backtick fence breaks when the info string has special characters"
A backtick-fenced info string can't contain a backtick character — the spec disallows it, to avoid the fence being misread as inline code. If your filename convention or a URL you're embedding needs a backtick, switch to a tilde fence (~~~) instead; tilde fences don't carry that restriction and can hold backticks or tildes in the info string without issue.
Doing this with Carets
Carets is a fast, native notes and code editor for iPhone, iPad, and Mac, and code blocks are one of the places that shows. Files, projects, and tags keep a growing set of snippets organized instead of scattered across scratch notes, and native syntax highlighting means a .ts or .py file reads the way it should the moment you open it — no waiting on a web wrapper to catch up.
Carets is built for iOS and Mac rather than adapted to them, so editing a README or annotating a snippet on an iPhone feels as direct as doing it on a Mac. And because everything stays plain text, a filename convention you settle on today — colon-separated line numbers, a bold caption above the fence, whatever fits your workflow — stays portable if you ever move your notes somewhere else.
Carets is available on the App Store for iPhone, iPad, and Mac.
Frequently Asked Questions
Is there an official Markdown syntax for filenames and line numbers in a code block?
No. CommonMark and GitHub Flavored Markdown define the info string mainly for a language identifier and explicitly leave everything past it up to the renderer, so no single filename or line-number syntax works everywhere.
What's the most common way to add a filename to a fenced code block?
The most widely seen informal convention places the filename right after the language, sometimes with a colon-separated starting line number (for example, ts app.ts:12 in the info string), echoing the colon notation already used in compiler and linter output — but support depends entirely on the renderer.
Why don't backtick fences support line numbers directly?
CommonMark treats line numbering as a rendering concern, not a document-structure concern; the practical path is a post-processing syntax highlighter like Prism.js or highlight.js with a line-numbers option turned on, rather than new Markdown syntax.
What's the safest way to label a code block's filename if my renderer doesn't support any convention?
Put the filename in plain prose or a bold line directly above the fence (for example app/controllers/todo.js) instead of packing it into the info string — it renders identically everywhere, including on GitHub, in static site generators, and in Carets.