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>
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
- Frontmatter
- Block Syntax
- Drafts
- Password-protected Entries
- Journal Studio
- Photo Essays on a Subpage
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 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 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:



| 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:

Photos in an entry open in the same lightbox as the rest of the site, with keyboard and swipe navigation.
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:
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 — 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>.mdin exactly the format documented above, with a backup of the previous version incontent/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
essayFiletakes a bare slug — letters, digits,-and_. A path such ascontent/essays/sample-story.mdis 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.




