Details, SEO and History
The editor's right rail: fields generated from your Zod schema, a search and social preview that mirrors as you type, and the last twenty saves.
The rail beside the document canvas has three tabs. All three write into the same save, and the pill above them tracks one dirty state across all of them.
Details
Every control here is generated from your content.config.ts. Nothing in src/admin.config.ts declares a field; it only names which existing field carries which role. Add a field to your Zod schema and the control appears. Rename one and the control follows. Delete one and it goes.
What each Zod shape becomes is covered in What the editor reads from Zod.
A field carrying a .default() shows it. A required field is marked required. The save posts only the keys you changed, and an absent value and an empty string stay different answers on the wire — so a rail-only edit re-emits no body at all, and a field you never touched is never rewritten.
The frontmatter patch is deliberately not schema-validated at save time. A draft may be half-written, and refusing to save an unfinished entry is the wrong trade. Validation is Publish’s job — a malformed entry blocks the push and is named per field.
Date inputs carry max="9999-12-31", because a browser date field will happily hold a year segment of 192026 and report itself valid. A control the platform flags invalid contributes nothing to the payload.
The web address
The rail’s footer shows where the entry lives on your published site, built from the collection’s publicPath. Edit renames it — see Working with entries for the two rules a rename follows.
Preview opens the entry on your site. In dev that is your dev server; deployed it is the staging URL you set in Settings, and the affordance hides entirely until that value exists, because only you know what URL your branch deploy got.
SEO
The tab appears when your configuration named at least one of the five SEO roles. It renders your real fields rather than an invented seo{} object — for a typical Astro blog, description is the search description, so that is the control it shows.
Three things worth knowing:
The search preview mirrors as you type. Each preview element is addressed by the field it mirrors, and the site-name suffix sits outside the mirrored span, so it cannot be overwritten by your title.
The social image field carries a live thumbnail, not a validity tick. A path is the one value nobody can proofread by reading it. A loaded thumbnail reports the file’s real dimensions; the 1200×630 beside it is advice, not a claim about your file. An empty field renders no src attribute at all — src="" re-requests the current page, which here would be the editor itself.
Social title and description are overrides with no boolean beside them. Empty means “same as search”. Untick the box and the inherited text appears as the placeholder rather than being copied into the input, so nothing duplicate is saved to go stale later. These reach the og: and twitter: tags only; the JSON-LD headline stays your real title.
A field claimed by both Details and SEO renders a control in each, and they write the same key — the two hand over to each other correctly, which is asserted in both directions by a test.
History
The last twenty saves of this entry, newest first, with the current one pinned. A snapshot is the whole frontmatter after a landed commit, plus its digest and its author.
Snapshots are keyed per entry, not per account — one timeline for two editors, the same way one git log is. ⌘S on an unchanged file records nothing, because no commit landed. What falls off the end of the twenty is a commit git still holds.
Restore fills the rail from the snapshot and re-dispatches the input events, which means it rides the same pipeline your typing does: the pill goes dirty, the autosave banks it, and nothing is written until you save. It is not a separate endpoint and not a git operation — it is the rail being typed into for you.
Drafts and autosave
Two seconds after your last edit, the rail banks the same payload as a draft. A draft is a save that has not been committed: { base, frontmatter, body?, savedAt }.
It lives on the server, in the accounts store, keyed to (your account, this entry). That is the meaningful difference from a browser-local draft — it follows you to another machine, and it cannot be lost by clearing site data. It was IndexedDB for four days and the decision was reversed.
One draft per account per entry. Two people drafting the same entry never meet here; they meet at save, where the conflict check catches it.
A landed commit discards your draft. Nothing else expires it — there is no eviction sweep, which is a known ceiling rather than a design.
An empty frontmatter patch is a real draft (a rail-only edit that changed nothing yet), so the record is never tested for truthiness.
Tags and lists
An array field renders as chips. Each chip carries its value as data, and every mutation dispatches an input event on the container — one pipeline, no second dirty flag, so adding a tag marks the entry dirty exactly the way typing does.
A patched list is rebuilt rather than written straight through, so it serializes as a flow list of quoted items instead of a block list of bare scalars.
Troubleshooting
The SEO tab is missing. Your collection names none of the five SEO roles in src/admin.config.ts. That is the honest answer rather than a tab of controls addressing fields you do not have.
A field shows Raw YAML. The Zod shape has no control the reader can be confident about. What the editor reads from Zod lists which shapes do that and why.
My social title keeps coming back. Ticking the override box back on writes an empty string rather than deleting the key, because the frontmatter patch never deletes. Empty is the same as absent for rendering purposes.
The thumbnail is blank but the path looks right. It is a real request for that file. A blank thumbnail with a plausible path means the file is not where the path says.
Restore did nothing visible. Check the pill — Restore fills the rail and marks it dirty. It does not save. Press Save.