Files
immich-folio/docs/journal.md
Ralksta 2f546f440d release: v0.11.0 (#459)
Ships `dev` as **v0.11.0**. `main` contains nothing that `dev` does not,
so this is a clean sync.

## Headline changes since v0.10.0

- **Journal & Journal Studio** — photo essays at `/journal`,
split-screen block editor with live preview, drafts, per-entry
passwords, cover images
- **First-run setup wizard at `/install`** — configure a fresh
deployment from the browser; credentials are verified against Immich
before anything is written, and the wizard is gated by a one-time token
- **About page editor and toggle** in the admin panel
- **Per-album photo order** independent of Immich, with a drag & drop
editor
- **Custom favicon upload**
- **Experimental portfolio features** — justified grid, `cover` hero,
unlisted subpages, per-album grid overrides, cover focal points,
external nav links
- **Security** — asset tokens length-capped before decoding (#423),
journal author markdown escaped rather than filtered, journal file paths
contained, admin session key derived with scrypt (#432), Next.js 16.3.0
for CVE-2025-13465
- **Fixed** — client proofing honours its own configuration again
(#454); journal photo rendering (#432)

Full detail in [CHANGELOG.md](CHANGELOG.md#0110--2026-08-15).

## Verified

`npx tsc --noEmit` clean · `npx vitest run` 435 tests pass · `npm run
build` succeeds · lint 0 errors · `prettier --check` clean · local
`docker build` of the release tree.

## After merge

Tag `v0.11.0` on `main` and publish the release — that triggers
`docker-publish.yml`, which pushes the image and moves `latest`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: ralksta <ralksta@users.noreply.github.com>
Co-authored-by: lancetm714 <108419425+lancetm714@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 09:09:18 +02:00

10 KiB

Journal & Photo Essays

Immich Folio has two ways to publish long-form, text-and-image storytelling. They share one Markdown format and one renderer, and differ only in where the result appears:

Feature Lives in URL Use for
Journal content/journal/*.md /journal/<slug> A running series of stories, listed on a /journal index page
Photo Essay content/essays/*.md the subpage's URL Turning one subpage of your gallery into a single narrative page

If you are unsure: use the Journal. A photo essay is the older mechanism and is tied to a subpage; the journal is a standalone section with its own index, cover images, reading times, and drafts.

Contents:

Journal Entries

Each entry is one Markdown file in content/journal/. The filename is the URL slug — content/journal/iceland-highlands.md is served at /journal/iceland-highlands.

A /journal link appears in the site navigation automatically as soon as at least one published (non-draft) entry exists. There is no setting to turn the journal on: an empty content/journal/ directory means no journal.

The journal index listing entries with cover images, dates and reading times

The index at /journal — cover image, title, date and reading time per entry.

A template ships with the project:

cp content/journal/sample-story.md.example content/journal/my-first-story.md

The template is an .example file on purpose — it references no asset IDs (those are specific to your Immich server), so copying it gives you a working skeleton rather than a page full of broken images.

Frontmatter

Optional YAML block at the top of the file:

---
title: 'The Icelandic Highlands'
subtitle: 'A visual journey across volcanic deserts and arctic silence'
author: 'Your Name'
date: '2026-08-05'
coverAssetId: 'asset-uuid-for-the-cover'
draft: false
password: 'clients-only'
---
Field Description
title Entry heading and browser/OG title. Defaults to the slug.
subtitle Subline under the heading; also used as the meta description.
author Shown in the entry byline.
date Publication date shown on the index and the entry.
coverAssetId Immich asset UUID used as the index card image and the OG preview image.
draft true hides the entry from visitors — see Drafts.
password Gates this single entry — see Password-protected Entries.

Reading time and the index excerpt are computed from the text; you do not configure them.

Every one of these fields is also a form control in the Journal Studio, behind Story Settings:

The Story Settings dialog: subtitle, author, publish date, cover photo, password and draft toggle

The same frontmatter, as a form — including the cover picker, the password field and the draft toggle.

Block Syntax

The parser is deliberately small. It understands headings, paragraphs, quotes and photos — one block per blank-line-separated chunk.

Headings

# Into the Wilderness

## Landmannalaugar

# through ###### are supported.

Text

Plain paragraphs. Inline **bold**, *italic* and [links](https://example.com) work; links open in a new tab. Everything else is escaped rather than passed through, so a paragraph can never inject HTML into the page.

Quotes

> Silence in the highlands is a physical weight. -- Anonymous

Text after a -- separator becomes the attribution line.

Photos

Photos are referenced by their Immich asset UUID, not by a file path:

![asset-uuid:fullbleed](Optional caption)
![asset-uuid:wide](Optional caption)
![asset-uuid](Optional caption)
Suffix Width
:fullbleed Edge to edge, breaking out of the text column
:wide Wider than the text column, but still inset
(none) Contained — same width as the text

Two UUIDs separated by a comma render as a side-by-side pair, sized to their real aspect ratios rather than forced equal:

![asset-uuid-left, asset-uuid-right](Optional caption)

Photos in an entry open in the same lightbox as the rest of the site, with keyboard and swipe navigation.

A rendered journal entry with heading, body text, a fullbleed photo and a quote

A rendered entry — headings, body text, a fullbleed photo and a pull quote, from the blocks above.

Tip

Writing asset UUIDs by hand is tedious. In the Journal Studio, Add Block → Photo opens the asset picker and fills the UUID in for you.

Drafts

draft: true in the frontmatter keeps an entry out of the /journal index and out of the navigation link check — but a logged-in admin still sees it, both in the index and at its URL. That makes the draft a preview of the real page rather than a separate preview mode.

Drafts are not secret: the entry stays reachable at its URL for anyone who knows the slug. Use a password if it must not be readable.

Password-protected Entries

A password in the frontmatter puts the entry behind the same gate used for subpages and albums. The unlock cookie is lb_auth_journal_<slug> and expires after 24 hours.

Store a hash rather than the plaintext where possible — the scrypt:salt:hash format described in Gallery Configuration applies here too. A plaintext password works but is logged as a warning at startup.

Journal Studio

The admin panel's Journal tab lists every entry — drafts included, marked as such — with its cover, date and password state:

The Journal tab listing entries with covers, dates and draft badges

Published and draft entries side by side; the lock marks an entry with a password.

Opening one gives a split-screen editor: blocks on the left, a live preview of the real page on the right, with a draggable divider between them (mouse, keyboard, and touch).

The Journal Studio: block editor on the left, live preview of the entry on the right

The Journal Studio — every block on the left, the real page on the right.

  • Add Block — heading, text, quote, photo, or photo pair
  • Photo picker — browse your Immich library and insert the asset UUID
  • Cover image, title, subtitle, author, date, draft, password — the frontmatter fields, as form controls
  • Reorder and delete blocks; each block carries a type chip so a long entry stays scannable
  • Save writes content/journal/<slug>.md in exactly the format documented above, with a backup of the previous version in content/journal/.backups/

Everything the studio writes can be edited by hand afterwards, and vice versa — the studio parses the same files.

Note

Entries written before the current photo syntax may contain position-based photo references. The studio marks those blocks instead of silently misrendering them, so they can be repointed at an asset UUID.

Photo Essays on a Subpage

A subpage can render a Markdown essay instead of an album grid. Point it at a file, inline the text, or let it build itself from the albums:

subpages:
  - name: Highlands
    title: 'Icelandic Highlands'
    subtitle: 'A Photo Essay'
    essayFile: 'sample-story' # content/journal/sample-story.md
    albums:
      - 'album-uuid-iceland-1'
Key Description
essayFile A slug, not a path — resolved as content/journal/<slug>.md, then content/essays/<slug>.md
essayText The essay Markdown inline in gallery.yaml — what the admin block editor writes
grid.layout essay — renders as an essay even without essayFile or essayText

Any one of the three switches the page into essay mode; setting essayFile or essayText implies it, so grid.layout: essay is only needed on its own.

Important

essayFile takes a bare slug — letters, digits, - and _. A path such as content/essays/sample-story.md is rejected (dots and slashes are not valid slug characters) and the page falls back to the generated essay below.

The Markdown format is identical to a journal entry, including the photo block syntax. The admin panel has a visual block editor for essayText under the subpage's settings.

The album list is used either way: it supplies the photos the lightbox pages through. And with grid.layout: essay but no Markdown at all, the essay is generated from those albums — one heading per album, followed by its photos, using each photo's Immich description as the caption.