Markdown Button: 3 Ways to Add Clickable Buttons

September 11, 2026 · 8 min read

Markdown Button: How to Add Clickable Buttons

Markdown has no markdown button syntax. Every button you see in a README or a docs page is one of three things: an image link (a badge), an inline HTML anchor, or a platform shortcode. This guide shows all three, explains which renderers keep them and which strip them, and gives you copy-paste examples for each.

Why Markdown Has No Button Syntax

The CommonMark spec (0.31.2, January 2024) defines links, images, emphasis, lists, code, and a handful of other constructs. Buttons are not among them. That is deliberate: Markdown describes structure, and a button is a presentation choice.

What the spec does give you is an escape hatch. Raw HTML passes through the parser untouched, so any button you can write in HTML you can drop into a .md file. Whether the renderer on the other end keeps that HTML is a separate question, and it's the question that decides which method you use.

So markdown buttons come in three flavours, ranked by how widely they survive.

  1. Image link (a badge from a service like Shields.io). Works everywhere images and links work.
  2. Inline HTML anchor. Works wherever raw HTML is allowed, with styling limits.
  3. Platform shortcode or component. Works only on the site generator that defines it.

A button is a link first, so the anchor rules in our Markdown links guide apply to all three. This post spends its words on the button-specific parts.

Method 1: The Badge Button (Image Link)

An image wrapped in a link is the only pure-Markdown way to get something that looks like a button. Nest the image syntax inside the link syntax.

[![Download](https://img.shields.io/badge/Download-v2.4.0-blue)](https://example.com/download)

That renders as a blue pill reading "Download | v2.4.0", and clicking it opens the download URL. GitHub, GitLab, Obsidian, and the major static site generators treat it as a normal clickable image.

The badge itself comes from Shields.io, whose static badge URL is https://img.shields.io/badge/label-message-color. Two escaping rules matter: an underscore or %20 becomes a space, and a double dash -- becomes a literal dash. So Get_Started-Read_the_docs-green gives you a "Get Started" label with "Read the docs" as the message.

You can also point at your own SVG or PNG stored in the repo. The Markdown image guide covers sizing and paths. The only button-specific advice: keep the image under about 40 pixels tall so it reads as a control, not a banner.

One accessibility note: the alt text inside the inner brackets is what a screen reader announces. Write [![Download the installer]...], not [![badge]...].

Method 2: The HTML Anchor Button

When your renderer allows inline HTML, an <a> tag gives you a real button with real text. The trick is knowing what the sanitizer keeps.

<a href="https://example.com/signup">
  <img src="https://img.shields.io/badge/Sign_up-free-brightgreen" alt="Sign up for free" />
</a>

That is the safest HTML form because it uses only tags and attributes GitHub permits. GitHub runs the HTML through a sanitizer. Per the github/markup README, it removes script tags, inline styles, and class or id attributes. The html-pipeline sanitization filter's default allowlist keeps a with href, img, p, kbd, details, and summary. It drops button, style, class, and onclick.

That has three consequences.

  • A <button> tag is stripped on GitHub. The text inside it survives as plain text, but nothing is clickable.
  • style="..." is removed, so you cannot colour or pad an anchor with CSS on GitHub.
  • align="center" on a <p> is kept, which is how you centre a button in a README.
<p align="center">
  <a href="https://example.com/docs"><img src="https://img.shields.io/badge/Read-the_docs-blue" alt="Read the docs" /></a>
</p>

If you control the CSS, as you do on a Jekyll, Hugo, MkDocs, or Docusaurus site, a styled anchor is the cleanest option.

<a class="btn" href="https://example.com/start">Get started</a>

Jekyll's default processor, kramdown, passes HTML blocks through unchanged by default (the parse_block_html option is false). Hugo is the exception: Goldmark's renderer.unsafe option defaults to false, so raw HTML is dropped until you set it to true in hugo.toml.

When you find yourself writing more HTML than Markdown, read our Markdown vs HTML comparison first. A button is fine; a whole layout in a .md file usually isn't.

Method 3: Shortcodes and Components

Static site generators let you define a button once and call it by name. The syntax is generator-specific, so it fails everywhere else.

Hugo uses shortcodes: a template in layouts/shortcodes/button.html that you call with {{< button href="/start" >}}Get started{{< /button >}}. Hugo does not run the output of the {{< >}} form back through the Markdown renderer (the {{% %}} form does that), so the HTML inside is never mangled.

Docusaurus and any MDX site let you import a React component and write <Button href="/start">Get started</Button> in the document. It's a component, not sanitized HTML, so styling and click handlers work.

MkDocs with Material supports an .md-button class on ordinary links via the attr_list extension: [Get started](/start){ .md-button }.

Our opinion: use a shortcode only when the same button appears on more than a few pages. For a one-off call to action, an HTML anchor is easier to read in the source and easier to move between projects.

Try Markdown Buttons in the Editor

The demo below contains all three approaches. The badge and the anchor render in the preview; the shortcode stays as literal text, which is exactly what happens on any platform that doesn't define it.

Three ways to make a button

1. Badge button (pure Markdown)

Download v2.4.0

2. HTML anchor, centred the GitHub-safe way

Read the docs

3. Hugo shortcode (only Hugo renders this)

{{< button href='/start' >}}Get started{{< /button >}}

45 words470 characters15 lines
Markdown

Run the demo through the Markdown to HTML converter and you'll see the anchor and image tags in the output, which is the quickest way to check your button before pushing it.

Where Do Markdown Buttons Render?

PlatformBadge image linkHTML <a> anchor<button> tagCSS stylingShortcode
GitHub READMEYesYesStrippedNo (style removed)No
GitLabYesYesSanitizedNoNo
Obsidian (reading view)YesYesRenders, inertVia CSS snippetNo
Jekyll (kramdown)YesYesYesYesLiquid include
HugoYesOnly with unsafe = trueSameYesYes
Docusaurus (MDX)YesYesYesYesReact component
Discord, SlackLink only, image may embedNoNoNoNo

"Inert" means the tag renders but a <button> with no JavaScript behind it does nothing when clicked. No Markdown renderer executes scripts, so a native button is never the right choice for a link. Use an anchor.

The one platform where this hurts most is a GitHub README, which is also where people most want a call-to-action button. The README guide shows badge rows and centred link buttons that pass GitHub's sanitizer.

Common Markdown Button Mistakes

Using a <button> tag for a link. It's stripped on GitHub and does nothing elsewhere. Write <a href="..."> and style it like a button.

Putting Markdown inside an HTML block. Under CommonMark, a block that opens with a tag such as <p> or <div> on its own line is raw HTML until the next blank line, so **Bold** written inside the centred <p align="center"> block above shows the asterisks. Use <strong> inside the tag or keep the button text plain.

Forgetting alt text on a badge. [![](url)](link) is a clickable image with no name. Screen readers announce the URL instead. Always fill the alt brackets.

Markdown Button FAQ

A markdown button is a link wearing a costume. Reach for a badge image link when the file might render anywhere, an HTML anchor when you control the site, and a shortcode when the same button repeats across pages. Paste the demo into the editor, swap in your own URL and label, and check the HTML output before you commit it.