Since Hugo will create a new directory for each of your post, it’s a bit complicated to properly reference your local images.

It’s best to follow the structure of Hugo’s generated files, which they call page bundles.

Local structure

content/
├── category1/
│   ├── post1/
│   │   ├── image1.jpg
│   │   ├── image2.png
│   │   └── index.md
│   └── post2/
│       ├── image21.jpg
│       └── index.md

Generated structure

hostname/
├── post1/
│   ├── image1.jpg
│   ├── image2.jpg
├── post2/
│   ├── image21.jpg

After using this structure, the images will be correctly rendered either in your local markdown editor or generated static Hugo page. Simply embed image1.jpg in post/index.md with ![](image.jpg), and it will be rendered as <img src="image1.jpg"> in hostname/post1.

However, on the home page where you see the previews of a list of (recent) posts, the image cannot be shown since the relative path to the image is not the same in the home page, which is the parent of the page resource.

To solve this, use Markdown Render Hooks.

Have this in themes/{your-theme}/layouts/_default/_markup/render-image.html or layouts/_default/_markup/render-image.html

<img class="img-zoomable" src='{{ if not (strings.HasPrefix .Destination "http") }}{{ .Page.RelPermalink }}{{end}}{{ .Destination | safeURL }}' alt="{{ .Text }}" />

.Page.RelPermalink is the absolute path to the file (with a trailing /), and .Destination is the original link of the image. To support both self hosted images & external images (starting with http/https), we use a simple if condition.

Now, <img src="image1.jpg"> will be automatically replaced by <img src="/post1/image1.jpg">, while <img src="https://i.imgur.com/AAAAA.jpg"> will stay the same.

Relevant discussions: