The syntax: one character away from a link
- The syntax is
, and the leading exclamation mark is the only thing separating an image from a link. - Markdown stores a reference, never the file, so a broken image is nearly always a broken path.
- Plain Markdown has no width setting at all - An HTML
<img>tag is the only portable way to size a picture. - One relative path can work on GitHub, break in a site build, and still look perfect in your local preview.
An image in Markdown is . It is the same shape as a link, with an exclamation mark in front. That single line is the entire syntax:

The three parts
The leading ! means "embed this, do not link to it". The square brackets hold the alt text. The parentheses hold the path or URL. Drop the exclamation mark and you get a text link instead of a picture. That is the most common typo here, and the reason it pays to know link syntax first.
Tip: To show the syntax on screen without rendering it, wrap it in a code block. Backticks switch every marker off, including the exclamation mark.
Adding a title

A quoted string after the path becomes the HTML title attribute. Desktop browsers show it as a tooltip. Most writers skip it, and with reason: touch devices never show it, screen readers treat it in different ways, and search engines ignore it. Use one for a photo credit, never for something the reader needs.
What it becomes
<img src="images/bike.jpg" alt="A red bicycle leaning against a brick wall">
That single line explains everything below. Markdown never copies, uploads or embeds the file. It stores a reference. So a broken image is almost always a broken path rather than broken syntax. See Markdown vs HTML for how the two languages fit together.
Alt text: what it is actually for
Alt text is not a caption and not a filename. It has two jobs. Screen readers announce it in place of the picture, so for a blind reader the alt text is the image. It is also what the browser prints when the file 404s, when a firewall blocks the CDN, or when someone reads your README in a terminal.
Write what the image shows

Compare that with . The first tells a reader who cannot see the picture what they are missing. The second tells them nothing at all. Three habits carry most of the weight:
- Describe the content, not the file. "Bar chart of sign-ups by month" beats "chart.png" every time.
- Skip the words "image of". Assistive software already announces that it is an image.
- Keep it to one phrase. A short sentence is plenty. A paragraph is a caption, not alt text.
Charts and diagrams get one extra rule. If the picture carries data your prose does not, put the finding itself in the alt text. An image is the one element in a Markdown document that cannot be read as plain text.
Tip: When the numbers matter more than the picture, write a Markdown table instead of pasting a screenshot of one. A table stays readable, searchable and copyable in every reader.
When empty alt text is the right answer

Purely decorative images should have empty brackets. That is correct, not lazy. Empty alt tells a screen reader to skip something meaningless. Writing ![divider] makes every listener hear the word "divider" for no reason. If removing the image would cost the reader nothing, leave the brackets empty.
Relative paths vs absolute URLs
An absolute URL starts with https:// and works from anywhere. The price is a dependency on a host you may not control. A relative path is resolved against wherever the Markdown file currently sits. Images then travel with a Git repository and survive every branch and fork - Until you paste the same Markdown somewhere the folder does not exist.
 same folder, then img/
 identical, the ./ is decorative
 up one folder, then assets/
 from the root, and this is the risky one
 works anywhere online
The same path in three places
Relative paths confuse people because "relative to what?" has a different answer in every tool:
| Path you write | GitHub repo view | Static site build (Hugo, Jekyll, Astro) | Local preview (VS Code) |
|---|---|---|---|
img/build.png | Resolves next to the .md file. Works. | Usually breaks: the built page sits at a different URL depth than the source. | Works if the folder really is beside the file. |
../assets/build.png | Works, and is common in docs subfolders. | Breaks. Build output has no ../ matching your source tree. | Works. |
/img/build.png | Resolved against the repo root. Fine on github.com, dead in a raw clone. | The correct form: a leading slash means the published site root. | Resolved against the workspace folder, which flatters you. |
https://cdn.example.com/x.png | Works, proxied through GitHub's image cache. | Works. | Works when you are online. |
img/My Photo.png | Broken. The space ends the path. | Broken. | Broken. |
Two rules that cover most projects
In a repository README, use paths relative to the file. In a static site, put images under static or public and reference them from the site root with a leading slash. The complete guide shows both patterns in a finished document.
Watch out: Never mix the two conventions in one project, and never treat a working local preview as proof. VS Code resolves a leading slash against your workspace folder, so it looks right up to the moment you deploy.
Reference-style images and clickable images
When a CDN URL is eighty characters of hashes and query strings, inlining it wrecks your source. Reference style moves the URL to the bottom of the file:
Our traffic doubled after the redesign:
![Traffic chart for 2025][chart]
[chart]: https://cdn.example.com/assets/8f2a/traffic-2025.png "Sessions per month"
Three things make that worth doing:
- The label matches its definition case-insensitively, so
[Chart]still finds[chart]. - Definitions can sit anywhere in the document, and readers never see them.
- One definition can serve any number of images, which suits a logo used in five places.
The same mechanism powers reference-style links, and both share one habit worth keeping: name the label after the thing, not after its position in the file.
Making an image clickable
[](https://ci.example.com/project)
Nest the image inside a link and it becomes the link text. Read it from the inside out. The  part is the image, and the [...](...) around it is the link. This one pattern powers every badge row and every click-for-full-size thumbnail on GitHub.
Sizing images, which Markdown cannot do
Markdown's image syntax has no width and no height. Not a shorthand, not an attribute, nothing. An image renders at its natural pixel size, which is why an untouched phone screenshot swallows an entire README. To set a size you have to step outside Markdown. Three workarounds exist, in descending order of portability.
1. Resize the file before you commit it
The boring answer is the best one. A 900-pixel-wide PNG loads faster, prints correctly and works in every renderer. It needs no special syntax, so nothing can strip it and nothing can ignore it.
2. Use a raw HTML img tag
<img src="docs/settings.png" alt="Settings page" width="420">
<p align="center">
<img src="docs/logo.svg" alt="Project logo" width="180">
</p>
This is the answer on GitHub, and the closest thing to a portable one. Nearly every renderer that allows HTML accepts it: GitHub, GitLab, VS Code and most static site generators. Three details decide whether it behaves:
- Set only
widthand the height scales with it. A percentage such aswidth="50%"works too. - Keep the tag on its own line, with a blank line above and below it.
- Markdown inside an HTML block is not processed, so do not nest other syntax in there.
Watch out: Strict renderers and publishing systems that sanitise HTML strip the tag outright. The image then vanishes rather than shrinking, which is one more argument for resizing the file. Markdown vs HTML covers which tools allow what.
3. Query strings, and what GitHub really supports
A query string is passed to whatever server hosts the image, so it only does something if that server understands it. An image CDN such as Cloudinary or imgix will resize on request:

GitHub understands one useful parameter, ?raw=true. It turns a repository page URL into the file itself:

That is the fix when an image shows as a broken icon because you copied the address bar from a file page. A blob URL returns an HTML page, not an image. The direct equivalent is raw.githubusercontent.com/acme/docs/main/img/chart.png. GitHub has no width parameter, so ?w=400 is simply ignored. It does support one fragment trick: #gh-dark-mode-only and #gh-light-mode-only give per-theme artwork.
Editor-specific shorthands
Several tools invented their own sizing syntax, and none of it is standard:
- Obsidian takes
. - Typora and markdown-it-imsize take
. - Pandoc takes
{width=50%}.
None of it renders outside its own tool, and the reader sees the raw characters instead. That is fine for private notes and wrong for anything you publish. The Obsidian guide is the place to weigh up its house extensions.
Where to actually put the file
Choosing a host is choosing who is responsible when the image disappears. Link rot is not hypothetical. Hosts change terms, free tiers close, and a URL that worked in 2019 is a broken icon today.
| Option | Best for | Link-rot risk | Honest trade-off |
|---|---|---|---|
Folder in the repo (docs/img/) | READMEs, project docs | Very low | Versioned, reviewable, works offline. Bloats repo size, and binary diffs are useless. |
| GitHub drag-and-drop into an issue or comment | Bug reports, quick screenshots | Medium | Zero effort, instantly hosted. The file lives outside your repo, so a clone or a move to another host shows nothing. |
Static site static/ or public/ | Blogs, docs sites | Very low | Deploys with the site, URLs are yours. Needs root-relative paths, which do not preview in a repo view. |
| Object storage or image CDN (S3, R2, Cloudinary) | Large or numerous images, resizing on the fly | Low, if the bill is paid | Fast, resizable, effectively unlimited. Costs money and adds a service to keep alive. |
| Free host such as imgur | Forum posts, throwaway sharing | High | Free and instant. Terms change, old uploads get purged, hotlinking may be blocked, and offices firewall it. |
| Hotlinking someone else's page | Nothing | Very high | Spends their bandwidth without consent, and they can rename, delete or replace the file with anything at all. |
For anything in a Git repository, commit the images. It is the only option where the picture and the prose are certain to stay together. That is the same reasoning that makes plain text worth using at all, set out in full in what Markdown is.
Tip: Keep every asset for a project in one folder, such as docs/img/. Scattered images are why moving a single file breaks half a README, and one folder turns the repair into a single find-and-replace.
SVG, GIF and formats that behave differently
The syntax does not care about file format. It only writes an src. Whether the picture displays depends entirely on the viewer.
- PNG and JPEG work everywhere. PNG for screenshots and diagrams, JPEG for photographs.
- Animated GIF works everywhere and animates by default, which makes it the standard way to demo a short UI interaction. Keep clips to a few seconds; a 20 MB GIF is a slow page for everyone.
- SVG renders on GitHub, GitLab and in browsers and stays crisp at any size, so it suits logos and diagrams. Scripts inside an SVG are stripped for security, and some renderers refuse SVG outright.
- WebP and AVIF are far smaller than PNG and work in current browsers and GitHub, but older tooling still chokes on them.
- PDFs and video are not images.
renders a broken image, not a player. Point at them with ordinary link syntax, or use GitHub's upload box, which turns a dragged-in video into a player.
When in doubt, choose PNG for anything containing text and JPEG for photographs. Both have worked in every tool for decades, and neither needs a fallback. Support varies by platform, and the cheat sheet lists which ones render images at all.
Common mistakes and how to fix them
Spaces in the filename
Broken: 
Fixed: 
Also works: 
Also works: 
The parser stops at the first space. Percent-encoding or angle brackets rescue a file that already exists. Renaming to hyphens stops the problem coming back.
Case sensitivity
Broken on a server: 
File on disk: img/logo.png
macOS and Windows filesystems ignore case by default. The Linux servers that build and serve your site do not. So a page that previews flawlessly on a laptop can deploy with every image broken. Adopt one convention - Lowercase and hyphens for every asset - And the whole class of bug disappears.
Full-width brackets from a Chinese IME
Broken: 
Fixed: 
A Chinese input method produces full-width punctuation, and Markdown recognises only half-width ASCII. The alt text can be in any language. The four punctuation marks around it cannot. Switch to half-width before typing syntax, or type the punctuation first and paste the text in.
The missing exclamation mark, and blob URLs
Broken: [Screenshot](img/app.png) renders as a text link
Fixed: 
Broken: 
Fixed: 
Both are one-character repairs. The first is a missing marker. The second is a URL that points at a web page rather than at the file behind it.
A fault table for the rest
| What you see | Usual cause | Fix |
|---|---|---|
| A text link where a picture should be | The ! is missing | Add the exclamation mark |
| Broken icon on GitHub, fine on your laptop | Filename case, or a blob page URL | Match the case exactly, or add ?raw=true |
| Broken icon on the live site, fine in the repo | Path is relative to the file, not the site root | Move the file under static/ and start the path with / |
| Alt text shows, the image never loads | Nothing is at that path | Open the URL on its own in a browser tab |
| The whole line prints as plain text | Full-width brackets from an IME | Retype the four punctuation marks in half-width |
Work down that table before blaming the platform. A wrong URL 404s the moment you open it, and a working one means the fault is in your Markdown. For a shorter walkthrough see how to add an image in Markdown.
Support data verified
Frequently Asked Questions
How do I resize an image in Markdown?
You cannot, not with Markdown syntax. Use a raw HTML tag such as <img src="a.png" alt="..." width="420">, which GitHub and most renderers accept. Resizing the file before you add it is safer still. Shorthands like  work only in the tool that invented them.
Why is my image showing as a broken icon on GitHub?
Three usual suspects. The path is relative to the wrong folder, the filename case does not match, or you pasted a blob page URL instead of a raw one. Linux servers care about case and your laptop does not. Add ?raw=true to a blob URL, then open the address on its own to check the file is really there.
Should I ever leave the alt text empty?
Yes, for purely decorative images. Empty brackets tell a screen reader to skip the image, which is exactly right for a divider or a flourish. For anything that carries meaning, describe what the reader would otherwise miss.
Can I put an image inside a table cell or a list?
Yes to both. Image syntax is inline, so it works inside a table cell and inside a list item. Watch the width in tables, since a full-size screenshot will stretch the column past the edge of the page.
Do images work the same on every platform?
No. GitHub, GitLab, Obsidian and VS Code support the full syntax. Discord and Reddit do not render it, and expect an upload or a bare URL instead. The cheat sheet has the platform-by-platform picture.
Does Markdown support video embeds?
No. Markdown has no syntax for video at all, and  gives you a broken image rather than a player. GitHub is the exception worth knowing: drag a video file into an issue, a pull request or a README and it renders a player, which is a GitHub feature and not part of Markdown. Everywhere else you need a raw HTML <video> tag, commonly stripped by the sanitizers platforms run for security, or a thumbnail image wrapped in a link that points at the video.
Keep learning
Try the Editor
Try the Editor