Search...Search plugins and themes...
⌘K
Sign in
  • Get started
  • Download
  • Pricing
  • Enterprise
  • Account
  • Obsidian
  • Overview
  • Sync
  • Publish
  • Canvas
  • Mobile
  • Web Clipper
  • CLI
  • Learn
  • Help
  • Developers
  • Changelog
  • About
  • Roadmap
  • Blog
  • Resources
  • System status
  • License overview
  • Terms of service
  • Privacy policy
  • Security
  • Community
  • Plugins
  • Themes
  • Discord
  • Forum / 中文论坛
  • Merch store
  • Brand guidelines
Follow us
DiscordTwitterBlueskyThreadsMastodonYouTubeGitHub
© 2026 Obsidian

Photo Row Layout

harderhengharderheng34 downloads

Lay images out the way a published site will: images written one after another in a paragraph become one equal-height row, with captions, optional EXIF, and grid / masonry / scroll blocks.

Add to Obsidian
  • Overview
  • Scorecard
  • Updates6

Write image-heavy notes and see the finished layout while you write — not after you publish.

  • Images written one after another in a single paragraph become one equal-height row. Mixed portrait and landscape shots share the row by width ratio, nothing gets cropped.
  • Captions come from the alt text, and camera data (EXIF) can be appended on a second line.
  • Real dimensions and multi-width srcset are used when they are known, so rows do not jump around while images load and the browser picks the right file for the screen.
  • ```photos fenced blocks give you grid / masonry / horizontal scroll and explicit row widths.
  • Works in reading view and live preview (live preview can miss the fence options if the block is nested very deep — it falls back to the default row).

The layout rules are shared with a static-site generator, so the preview is what the built page will show.

Install

Community plugins — search for "Photo Row Layout" in Settings → Community plugins → Browse.

Manually — download main.js, manifest.json and styles.css from the latest release into <your vault>/.obsidian/plugins/photo-row-layout/, then enable the plugin and reload Obsidian.

How to write

Ordinary Markdown. Nothing to switch on:

![Bridge at dawn](/img/photos/post-images/9a12ae1134/1920.webp)

![Bridge at dawn](/img/photos/post-images/9a12ae1134/1920.webp)
![The old harbour](/img/photos/post-images/f3c5b8b5e4/1920.webp)
What you write What you get
One image per paragraph A single full-width image
Several images in the same paragraph (single line breaks, no blank line) One equal-height row
Images separated by blank lines One row per image
![Caption text](…) Caption under the image
An empty alt text No caption

Fenced blocks

Use a ```photos block when you want to control the layout. Options go on the info line:

```photos wide 2
![](/img/photos/post-images/9a12ae1134/1920.webp)
![](/img/photos/post-images/f3c5b8b5e4/1920.webp)
![](/img/photos/post-images/ce710a1bf0/1920.webp)
![](/img/photos/post-images/ede3dc2183/1920.webp)
```
Option Effect
col (default) Keep the row inside the text column
wide / full Let the row break out of the text column (exact widths depend on your site's CSS)
grid / grid 4 Grid with square crops, 3 columns by default
masonry / masonry 3 Masonry, each image keeps its own ratio
scroll Horizontal scroll, one image per screen
crop / nocrop Force or forbid crop-to-align
ar=3/2 Crop everything to one ratio
2 3 4 Images per row (more than that wraps onto further rows)
# comment Ignored line

Fence options cannot be set on plain paragraphs — those always use the default row.

Settings

Setting What it does
Album index URL Optional. A JSON map of image hash → dimensions, variant widths and EXIF, fetched once at startup. With it, rows are planned before the images load, using real ratios and captions. Without it, each row is planned from the image's natural size after it loads.
Site base URL Optional. Prefix added to root-relative image paths (/img/photos/…) so previews can load them. Leave empty to leave paths exactly as written.
Show capture data under captions Camera, lens, focal length, aperture, shutter and ISO, when the index provides them.
Image upload Optional, off by default. When enabled, a pasted or dropped image is posted to your own endpoint and the path it returns is inserted instead of a file landing in your vault.
Direct base URL Optional. When this base answers, previews load images from it instead of the site base — handy for a LAN or CDN mirror. Probed at startup and every 3 minutes, cached per probe.

Album index format

Any URL returning a JSON object keyed by image hash works:

{
  "9a12ae1134": {
    "album": "post-images",
    "width": 7952,
    "height": 5304,
    "widths": [640, 1280, 1920],
    "hasAvif": false,
    "exif": {
      "camera": "SONY ILCE-7M3",
      "lens": "FE 35mm F1.8",
      "focal": "35mm",
      "aperture": "f/1.8",
      "shutter": "1/250",
      "iso": 100
    }
  }
}

An image URL is matched against the index when it looks like …/<album>/<hash>/<width>.webp (or .avif). Everything else is laid out from its natural size.

Upload endpoint (optional)

The plugin sends a POST with the raw image bytes:

POST <upload URL>?album=<album>&name=<url-encoded file name>
Content-Type: application/octet-stream
Authorization: Bearer <token>

It expects a JSON response such as:

{
  "ok": true,
  "hash": "9a12ae1134",
  "album": "post-images",
  "alt": "",
  "width": 7952,
  "height": 5304,
  "widths": [640, 1280, 1920],
  "url": "/img/photos/post-images/9a12ae1134/1920.webp",
  "exif": { "camera": "SONY ILCE-7M3" }
}

url is inserted into the note; the metadata is used right away for layout, so the new image is laid out like the rest without waiting for the index to be fetched again. If the request fails, nothing is inserted and the image stays on your clipboard.

Network use and privacy

No telemetry, no analytics, no accounts, and no data leaves your vault unless you configure something that asks for it. The plugin only talks to the URLs below, all of which you set yourself:

Request When Why
Album index URL Once at startup (and when you press Reload) Fetch image dimensions, variant widths and EXIF, so rows can be planned before the images load
Direct base URL One reachability probe at startup, then every 3 minutes while configured Decide whether previews should load from that base instead of the site base
Upload URL Only when you paste or drop an image and uploading is enabled Send the image to your own endpoint, which returns the path to insert

Leaving those fields empty means the plugin makes no requests at all.

Development

The row engine is a mirror of the one used by the site generator this plugin was written for, and the two are kept in sync by an equivalence test against the same album data. If you fork this and change the engine, keep main.js's row-engine section self-contained: it must stay free of Obsidian APIs so it can be unit-tested in plain Node.

Credits

The layout is modelled on hexo-theme-linen by LynanBreeze (MIT): row gaps, caption sizing and the crop/equal-height behaviour follow its .image-grid rules. The row engine in this plugin is a reimplementation kept in step with a static-site generator's version of the same rules, not a copy of that theme's code.

License

MIT — see LICENSE.

HealthExcellent
ReviewSatisfactory
About
Compose image-heavy notes and preview the final photo layout as you write. Group images in one paragraph to form equal-height rows that preserve aspect ratios and use alt text for captions; use photos fenced blocks for grid, masonry or horizontal-scroll layouts and rely on srcset to avoid layout jumps.
ImagesFormatting
Details
Current version
0.1.5
Last updated
2 weeks ago
Created
3 weeks ago
Updates
6 releases
Downloads
34
Compatible with
Obsidian 1.4.0+
Platforms
Desktop, Mobile
License
MIT
Report bugRequest featureReport plugin
Author
harderhengharderhengharderheng
GitHubharderheng
  1. Community
  2. Plugins
  3. Images
  4. Photo Row Layout

Related plugins

Image Captions

Add captions to images with inline Markdown and link support. The caption format is compatible with the Commonmark spec and other Markdown applications.

Admonition

Admonition block-styled content.

Linter

Format and style your notes. Linter can be used to format YAML tags, aliases, arrays, and metadata; footnotes; headings; spacing; math blocks; regular Markdown contents like list, italics, and bold styles; and more with the use of custom rule options.

Editing Toolbar

The Editing Toolbar is modified from cMenu, which provides more powerful customization settings and has many built-in editing commands to be a MS Word-like toolbar editing experience.

Ink

Handwriting and drawing directly between paragraphs using a digital pen, stylus, or Apple pencil.

Sort and Permute lines

Sort and Permute lines in whole file or selection.

TTRPG Tools - Maps

A toolbox for interactive fantasy maps.

Featured Image

Automatically set a featured image property in your notes based on the first image.

Imagine

A comprehensive image management plugin for managing, inserting, resizing, viewing, and batch processing images.

Pretty Properties

Makes note properties look more fun: adds side image, banners, list property colors and allows to hide specific properties.