Edit text right on the live page. Double-click, type, save.

1.11.2

October 11th, 2026

  • N/A Changelog not available.

1.11.1

October 6th, 2026

Fixed

  • Keyboard focus on a field is visible on every field: a solid ring, offset from the dashed "editable" outline. It used to show only through the kind badge, which fields narrower than 140px never get.
  • Focus no longer drops to the top of the page. Escape or Enter in a text field leaves it on the field; closing the rich editor with Escape gives it back to the field; after the control panel overlay or a field opened in place closes and the page reloads, the field that opened it is focused again. Switching edit mode on from the keyboard moves focus to the first visible field, switching it off moves it to the button that switches it on.
  • The select, date input and switch in the small panel, the markdown source box and the one box of a single-value window are named after their field (aria-labelledby), so a screen reader says which field it is.
  • A save error is shown in full, wrapped onto a second line rather than cut at 40 characters, and the status message is a live region (role="status"), so "Saved" and every refusal are announced.

Changed

  • composer require is the whole install on a standard Statamic site. The front-end files are now also published under the addon's own tag (statamic-inline-edit), which Statamic publishes with --force after statamic:install. A site that does not run that from Composer needs one command, php artisan vendor:publish --tag=statamic-inline-edit --force. The statamic-inline-edit-assets tag still works. Because of --force, hand edits to the published files in public/vendor/statamic-inline-edit/ are now replaced on every composer install and composer update; style the editor from your own stylesheet instead.
  • On a phone, a save message gets its own row above the buttons.
  • The bar's hint says that Enter opens a field, too.
  • MARKETPLACE.md is no longer part of the Composer package.

1.11.0

October 2nd, 2026

A small window for text that cannot be typed into in the line

A headline built from two cells, a sentence with a link in it, a word drawn as emphasis, a quote wrapped in quotation marks, the same label in six places: the page shows these values changed, so typing into the element would save the drawing. They now open a window instead.

InlineEdit::popup($entry, ['rows.a1.text' => 'Line 1', 'rows.a2.text' => 'Line 2'], 'Hero headline');
InlineEdit::popup($testimonial, ['quote', 'author_name']);
InlineEdit::cell($entry, 'rows', $row['id'], 'text', 'Lead', popup: true);
  • New mode popup (data-sie-popup carries the boxes: address, label, stored value, whether it has lines). One box per value, a one-line input or a textarea as tall as the value; only boxes that changed are sent, each under its own address, through the existing save route and its gates. The page reloads after the save.
  • Addresses are field handles or grid cells of one entry, plain text only, the same gates as a text marker (permission, no revisions, not forbidden, not read-only/hidden/computed, not a picture row). One address failing refuses the whole marker.
  • Pending changes survive closing the window (Escape, Done, a click outside), the window's own Save is the bar's. Ctrl/Cmd+Enter saves from a box.
  • The pending value shows as a small note above the element, not at the size of the headline it sits on.
  • While editing, a click on a link inside a window element is not followed, and a double-click opens the innermost marker only (a button label inside a sentence opens itself, not the sentence's window).
  • InlineEdit::supports('popup').

The picture card has no "Set Alt"

Core's asset field shows a "Set Alt" badge on the tile, which writes the asset's own meta, a place the page never reads. Next to the card's alt text field it looked like the same thing. Off on the picture card only (show_set_alt: false), core's switch; the asset field everywhere else in the control panel keeps it.

1.10.0

October 2nd, 2026

Alt texts on the picture card

An alt text is never on the page, so there was nothing to double-click. It now sits where the picture is dealt with anyway: under the asset browser on the card that opens on a picture cell, as a text field "Alt text".

InlineEdit::cell($entry, 'rows', $row['id'], 'value', 'Hero picture', image: true, alt: $altRow['id']);
  • The alt row is another row of the same grid, same column. Its id is signed into the card's address together with the picture's, so the field writes that cell and no other; moved onto another row the signature fails (403).
  • The marker leaves the alt out when the row would not fit: missing, the picture's own row, or a row the site calls a picture (imageCells). The card then is the picture alone, as before. Gone or turned into a picture between rendering and saving is 404.
  • Saving sends both fields. A picture that is already there is not written again, an alt text that did not change is not written either, so saving only the alt text changes exactly one line of the entry. An empty picture next to an alt text means "leave the picture" (the picker opens empty for a path outside every container). A bad picture writes neither; an alt text that is not a string is 422. Both go through the column's rules first.
  • Why a row and not the asset's own alt field: the same photo stands in different places for different reasons, and an asset's meta lives next to the file, not in the entry; on a site whose content is versioned and the uploads are not, it would not travel with the page.

Cells the page draws differently: source: true

A text cell whose page shows its value changed (*word* drawn as emphasis, a soft hyphen, two cells as one line) could not be edited safely: the save sent the drawing and wrote it over the source. With source: true the marker carries the stored value as data-sie-source; while the cell is edited the element shows that instead, opening and leaving it unchanged is not a change, and Escape or leaving it untouched puts the drawing back.

Smaller

  • InlineEdit::supports('alt' | 'source'), for a site that has to run on more than one version of the addon.
  • The one-field card posts every field of its blueprint, not just the first.

1.9.0

October 1st, 2026

Pictures

Double-click a picture in edit mode, and the control panel's own asset browser opens: choose another one or upload one, save, and the page reloads with it. A new mode, image, for two kinds of target:

  • An assets field on the entry (new config list images, default ['assets']). It opens the one-field card with core's asset field, the browser already open, and is saved exactly as the control panel saves it. Under revisions it falls back to the whole entry form, as everything does.
  • A text cell in a grid that holds the path of a picture, the shape a site takes when its pictures live as token rows: InlineEdit::cell($entry, 'tokens', $rowId, 'value', 'Hero', image: true). The cell opens the same browser through a new control panel route, /cp/inline-edit/image/{collection}/{entry}/{grid.row.column}, and what is written is the public URL of the chosen asset, nothing else: not a typed string, not a foreign URL, not an asset from a container missing from the new image_containers list (default ['assets']), never a private one. The route's address is signed by the marker, so a cell the page never offered as a picture (the headline beside it) cannot be written through it. Same gates as a text cell otherwise: entry policy, revisions, an inherited grid in a localization, a duplicated row id, read-only columns. Only an asset the person may view is taken, and a container from the list that does not exist or is private is skipped rather than fatal.
  • InlineEdit::imageCells(callable) tells the text save route which rows hold pictures, so it refuses every cell of them. The image: true on a marker speaks for one page; the text route is stateless and would otherwise take a foreign URL posted from the console. Row-wide, because a test that looks at a neighbouring column could otherwise be undone in two requests.

On the page: a picture is never an "empty field" (no chip over the image), gets the badge "Picture" (drawn on a box with a background picture; an <img> cannot draw pseudo-elements and keeps the outline only), takes the pointer cursor, and a click on a picture inside a link no longer follows the link while edit mode is on. The card takes the screen while the asset browser is open and gives it back after.

The card had no portal targets

Core's stacks and modals teleport into #portal-target-<id>, which the full control panel layout draws and the chrome-less card did not. The asset browser, an asset's editor and any other stack had nowhere to go; Browse threw inside Vue and showed nothing. The card's layout now draws the targets, including core's stacks-on-stacks class, without which a stack is zero pixels tall.

1.8.1

October 1st, 2026

A grid list stood one letter per line in the editor

1.8.0 copied grid-template-columns along with a list's grid. Its computed value is the resolved track in pixels, read on a probe that is shrink-to-fit, so every item in the editor was a few pixels wide. It is no longer copied; a list gets one implicit column the editor's width.

Escape on an untouched field could count as a change

Escape wrote the remembered text back as plain text. Where the template had dressed a value in markup of its own (a line break, a nowrap span), that flattened it, the text then read differently from its baseline, and opening and leaving a field without typing showed "1 unsaved". Escape now restores the markup as it was.

1.8.0

October 1st, 2026

Lists drawn with grid and ::before looked bare in the editor

A page that spaces its list items with display: grid; row-gap on the ul and draws its markers as li::before (list style off, absolutely positioned pseudo element) still looked different in the in-place editor: no gap, no marker. The probes now also read display, row-gap, column-gap, grid-template-columns and flex-direction of lists (only block, grid and flex), position of list items and quotes, and the ::before and ::after of li and blockquote whenever they have content. Nothing else gets a pseudo rule, so the editor's own placeholder and handles are left alone.

One cell of a grid can be edited on the page

InlineEdit::cell($entry, $grid, $rowId, $column, $label = null) marks one cell: the row with this id, this column. For a site whose copy lives in grid rows (a headline, a button label, a list item, each its own row), which had no way in until now.

  • The row is named by the id core persists on it (RowId), never by its position, so a row moved in the control panel between render and save cannot pull the edit onto its neighbour. Rows without an id (written by a script, never saved in the control panel) get no marker.
  • The address travels as the field handle grid.rowId.column. The script in the browser is unchanged; it sends what the marker said.
  • Plain text columns only (the fieldtypes list). Toggles, selects, markdown and assets inside a row get no marker and are refused on save.
  • The save route checks every cell from scratch: a real grid on the blueprint, a text column, a row that carries this id right now, the ceiling, the column's own validation rules. It writes that one value into the stored rows and nothing else; the grid's own process() is not run, because it rebuilds every row.
  • Entries under revisions get no cell marker, as the save route refuses them.
  • Refused as well, marker and save alike: a row id that occurs twice in the grid (no guessing which row), a column the blueprint marks read_only, hidden or computed, and a localization that inherits the grid from its origin (one cell would copy the whole grid into the localization).

Known limits:

  • Validation sees only the one column. A rule that compares columns of the same row (required_if, same) cannot see the other cells and does not fire.
  • Clearing a cell stores "", not null as the control panel does for an emptied text field. Both read as empty.

1.7.2

October 1st, 2026

The save bar vanished while a field was open in place

With the bottom bar up ("Edit page"), opening a Bard in place took the bar away, and the frame's own Save button sits under the whole field. On a long Bard that meant scrolling a screen away from the words to find it.

The bar now stays while a field is open in place, and its buttons work on that field: Save tells the frame to save (the frame runs the same action its own button does, with the same validation, and answers saved as before, which closes and reloads), Discard closes the field the way the frame's own close button does. The buttons are enabled in that state even at a change count of zero, because there is exactly one thing to save and it lives in the frame. The frame's own buttons stay. The frame only accepts such messages from the window that framed it, marked statamic-inline-edit-host, the same handshake the typography already uses.

Bard content in the editor did not take the page's own shapes, lists worst

The typography the page hands to the in-place editor is read from probe elements, and the probes missed the forms in which a page really styles a list:

  • Lists indented by margin-left (rather than padding) sat flush left in the editor; the margin was never read.
  • ProseMirror wraps every list item's text in a paragraph, and Bard's augmented output puts that same <li><p> on the page. A page that resets the paragraph margin inside list items (the common prose reset) had the editor take the ordinary paragraph margin instead, spreading every item apart. There is a probe for the paragraph inside an item now.
  • A list nested in a list item took the outer list's indent and marker.
  • The list marker itself is a pseudo element: a page styling li::marker in its own colour or size was not copied. Chrome and Safari expose the marker's computed style; on Firefox the marker keeps inheriting from the item, which is what it does on the page when nothing is said about it.
  • A quote lost its margin-left indent, and a horizontal rule kept the frame's own bottom border where the page draws only the top one.
  • Inline marks (links, strong, em, code) were probed as bare children of the field instead of inside a paragraph where they occur, so each came back one font size too small, and code was hit by the monospace default size on top of it.

The browser suite now compares the computed styles of ul, li, li > p, ul ul, li::marker, blockquote, hr, a, strong, em and code between the page and the editor over it, against a fixture page that styles its lists its own way. Nine of those comparisons failed before, none after.

1.7.1

October 1st, 2026

Inserting a Bard set in inline/cp mode failed with 403

Pressing "+" in a Bard opened in the page or in the one-field panel and picking a set did nothing; the request behind it was refused with a 403. Any addon that asks the form's blueprint for a field the same way, Bard Assist among them, failed with it and showed "Unavailable".

To add a set, Bard asks the control panel for its defaults and proves it may with the blueprint token the form carries. The control panel looks that blueprint up by name. The one-field form is built from a single field and had no name, so there was nothing to find. The token now names the entry's real blueprint, which is where the field lives. The form itself still holds only that one field, and saving still validates only that one field.

The obvious fix, giving the one-field blueprint the real one's name, was tried and turned down: a blueprint's cache is keyed by its name, so the single field would have overwritten the real blueprint for the rest of the request, and a save listener or a search index reading it there would have seen one field. A test now keeps it that way.

Where the marker goes

The README now says it: put the marker on the element that holds the text, not on a wrapper with padding. The open editor starts at the marked element's outer edge, so on a padded card it sits off the page's lines by the padding.

1.7.0

September 23rd, 2026

The toolbar comes with the selection and goes with it

A bar that stands there whether or not anybody is about to use it is the one thing an editor inside a page must not look like. It says "form", and the whole promise of this mode is "page".

Bard already has the other mode, so in place the field is opened in it: toolbar_mode: floating. Select a few words and the buttons appear over them; let go and they are gone. The blueprint's own setting is left alone, because it is answering a different question — how the field should look in the control panel, where a docked toolbar is right.

Room is reserved for it all the same. That toolbar is drawn inside the frame, and the frame ends where the text does: a selection on the first line would put it half outside and it would be cut off. So the form carries a top padding, the page reads it and pulls the frame up by exactly that much, and the strip hangs over what is above the article, empty until something is selected.

Two things that were wrong and only a screenshot showed:

  • The frame's blue focus ring. Stripping border and box-shadow was not enough; the control panel's ring is neither on its own. A blue rectangle around a paragraph is exactly the edge this mode exists to remove.
  • The room below was the toolbar's room, counted twice. The spacer took the whole difference between the frame and the block, but the part above the block hangs over the page and needs none. The gap under the text was one toolbar too tall.

Save and Close keep their strip under the text. They have to be reachable the whole time, not only while something is selected.

1.6.2

September 22nd, 2026

  • N/A Changelog not available.

1.6.1

September 22nd, 2026

  • N/A Changelog not available.

1.6.0

September 22nd, 2026

A site that draws itself keeps its markers

1.5.0 gave those sites a marker. This gives them the second half, which the first release got wrong in a way only a real site could show.

On a site whose pages are drawn client-side, a new page is not a new request. Going from a list to an article is a fetch that returns JSON, so nothing is injected into it, and the markers that arrive with it were born after the script ran. The editor read the document once, found them the first time by waiting, and was silently dead from the second page on. Double-click, nothing, no error.

So nothing is read once any more. Every marker goes through rescan(), which runs at boot and again whenever the document changes — batched to the end of the task, because a framework rendering a page touches the document a few hundred times. Markers that leave with their page are let go, so the bar stops counting unsaved changes in a document nobody can see.

And the script has to be on the page before those markers appear, which on such a site means before there is anything to edit. New config:

#89DDFF;">'inject_for_signed_in#89DDFF;">' #89DDFF;">=> #89DDFF;">true,

Off by default. On, the editor is placed on every page a signed-in user opens, and shows nothing on a page with no markers on it. The price is stated rather than hidden: the script carries a CSRF token, so those pages are marked uncacheable. On a site whose pages Statamic does not serve, that costs nothing.

Two things a router can ask, for the cases the observer cannot see:

window#89DDFF;">.StatamicInlineEdit#89DDFF;">.#82AAFF;">rescan() #676E95;">// I just changed the page
window#89DDFF;">.StatamicInlineEdit#89DDFF;">.#82AAFF;">dirty() #676E95;">// how many fields have unsaved text in them

dirty() exists because leaving a page is a navigation on such a site, not an unload — beforeunload never fires, and three rewritten paragraphs go with it without a word. Ask it in your router's before-hook.

1.5.1

September 22nd, 2026

Three findings from the Gauntlet rounds that 1.5.0 shipped without. Each one is a case the first version could not see because nobody had opened it on a page that had the problem.

The controls had to stop covering the page

They floated over the content, and what they floated over was another editable field and a line of the article. "Everything but the controls looks the same" cannot mean the controls delete what they cover.

Now they sit in a strip under the text — toolbar on the left, Save and Close on the right — and the page makes room for exactly that strip. What that costs is measured rather than assumed: an element between two paragraphs stops their margins collapsing into one another, which is sixteen pixels the page would otherwise gain for nothing, so the strip takes it off its own height.

The toolbar moves under the text through order on a flex column, so core's markup is not touched. It goes on the element that actually holds it, found through :has(> .bard-fixed-toolbar) — naming .bard-editor instead puts the toolbar straight back on top of the text it was meant to move out from under.

The editor sets the text in the page's own font file

Every font property matched and the same sentence still measured two and a half pixels narrower in the frame, which over a line is a word climbing into the row above. It was not a property that was missed. The control panel ships its own Inter and so does half the web; font-family: Inter in the frame asks for a different file than the same words ask for on the page, and two builds of a typeface do not have the same advances.

So the page's own @font-face rules travel with the typography, renamed so nothing can claim them, with their URLs made absolute. Where the rules cannot be read — a stylesheet from another origin, which is how most sites load Google Fonts — the frame asks that stylesheet for itself, and being declared last is what makes it win. Only from hosts that serve font declarations and nothing else; an unknown CDN stylesheet would be a whole framework's reset going through the control panel's interface.

Measured after, not before: the same sentence, 264.59 pixels in both.

The markers do not have to be in the HTML yet

A site drawn by React or Vue sends an empty root and a blob of props, and the markers appear when the application mounts — which is after a deferred script has run. Reading the document once and giving up found nothing, so the editor never appeared on exactly the pages the headless marker had been added for.

It waits for them now, for fifteen seconds. A client-side navigation to another page inside the same application is not covered, and says so in the source.

1.5.0

September 22nd, 2026

A Bard opens where it stands

Until now a Bard opened as a card in the middle of the screen. That was the right answer for a Grid or an asset picker and the wrong one for the field that holds the article: a Bard is not a field on a page, it is the page. On a card it has a different column width, a different typeface and a different measure, and you cannot see what you are writing.

Now the same real control panel field — same fieldtype, same validation, same save, sets and all — is put over the block it belongs to. The block keeps its box and stops being drawn, the frame paints nothing of its own, and the page around it is untouched. Configured in inline, which starts as ['bard'].

The typography is not approximated. The page measures the element that was double-clicked and one probe for each kind of block a Bard can produce — paragraph, h2, h3, h4, list, list item, quote, link, bold, italic, code, rule — and sends the computed result to the frame as rules scoped to the editor. Nothing of the site's stylesheet is loaded into the control panel, which would put a site's own reset through the control panel's interface.

What the frame carries with it is taken off on the way in: the box around the field, the label, the instructions, the editor's own padding and background. The toolbar becomes a floating panel as wide as its buttons, and Save and Close a second one — light panels over the content, the same rule the selection toolbar has followed since 1.2.

Three things found while building it, all of which the first version got wrong:

  • An iframe cannot take a block's place. A heading's top margin collapses out through its parent; a replaced element has no children and cannot collapse anything, so the gap above closes by exactly that margin and the rest of the article slides up. Eight pixels on the test page, and a different eight on every site. The block therefore keeps its box and the frame is positioned over it.
  • The chrome must not be in the flow either. With the toolbar and the buttons taking room, opening the editor pushed everything below down the screen, starting with the paragraph that had just been double-clicked. The frame is taller than the text and hangs over what is above and below instead.
  • Measure after the rules arrive, not before. The rules change the toolbar's padding and the editor's, so numbers reported before they landed describe a layout that is already gone — and the text ends up a few pixels off the line it belongs on.

While a field is open in place, the page's own bar steps aside. Two buttons saying "Save", one of them greyed out, is a question nobody should have to answer.

Known: the frame covers a band above and below the text while it is open, and a click there does not reach the page — an iframe cannot let a click through part of itself. The block keeps the height it had, so the page does not reflow while the text grows; the reload after saving puts it right.

The marker without a template tag

A Statamic site is not always Antlers. The content stays, the control panel stays, and the pages are drawn by React through Inertia, by Blade, or by a front end of its own. Those sites could not use this addon at all: the only way to mark a field was a template tag in an engine they do not run.

#676E95;">// wherever you build your props
#89DDFF;">'edit#89DDFF;">' #89DDFF;">=> #89DDFF;">[#89DDFF;">'content#89DDFF;">' #89DDFF;">=> InlineEdit#89DDFF;">::#82AAFF;">marker#89DDFF;">($entry#89DDFF;">, #89DDFF;">'content#89DDFF;">'#89DDFF;">)],
#89DDFF;"><div#89DDFF;"> {...edit#89DDFF;">.content#89DDFF;">} dangerouslySetInnerHTML#89DDFF;">={{ __html#89DDFF;">: article#89DDFF;">.content #89DDFF;">}} />

Same decisions, same refusals, same permissions — the gates moved out of the tag into Support\Marker, which both callers now use. A second copy for the headless path is how one of the two ends up more permissive than the other.

Such a site also has to name its own route group in middleware_groups, which starts as ['statamic.web']. Pages a site's own controllers serve are in web, and nothing would inject the editor there or mark those responses uncacheable. Naming both is safe: Statamic's frontend controller adds statamic.web on top of web, so its pages pass through twice, and the second pass leaves the script it finds alone.

A marker that leads nowhere is not drawn

slug, published, id, date, author, parent, blueprint: the save route and the one-field form have always refused these, but a marker was still drawn on them, and double-clicking it opened a panel that answered 404. A broken promise is not a safe default.

1.4.1

September 22nd, 2026

The one-field panel is a white card, not sometimes a black one

The card took its background from the full-screen overlay it grew out of, which is near-black. The control panel page inside it paints no canvas of its own, so every moment the frame had not painted showed a black card with the form's own dark labels invisible on it. Caught on the demo during a screenshot run, where it lasted long enough to be photographed.

The card paints its own surface now. One line, and it removes the whole class of it.

1.4.0

September 22nd, 2026

A Bard opens as a Bard, not as the whole entry form

Adrian, at the end of a session: "Ich fände es eleganter wenn man nur das bard Textfeld hat und nicht das ganze Statamic Input Feld als popup."

He was right. Double-clicking a paragraph handed back the entire control panel page in a full-screen frame: a sidebar, a revision history, a publish state and nineteen other fields, for one edit to one text. The frame was the size of the screen because its contents were the size of a back office.

There is now a control panel route that renders one field as a real publish form — /cp/inline-edit/field/{collection}/{entry}/{handle} — and the overlay opens that instead. The field is the real fieldtype with its real metadata, so a Bard arrives with its whole toolbar, its sets and its link browser, and saving runs through the blueprint's own validation. The panel is a card the size of the field, which the form measures and reports, rather than a black rectangle over the page.

Entries under revisions keep the old behaviour. Writing one field past a working copy would publish straight to the site on a collection whose whole point is that somebody approves first. Those still open the full form, which knows how to make one. The tag does not offer the new route for them, and the route refuses them a second time.

This adds a small control panel bundle to the addon, published with php artisan vendor:publish --tag=statamic-inline-edit.

1.3.0

September 22nd, 2026

The control panel for a toggle, a select or a date is a light panel now

Adrian pointed at a screenshot: a dark pill lying across the table row it was editing, holding the words "Belegung ·", a native select and a button marked Done. Roughly 660 by 110 pixels to change one word.

It is 192 by 69 now, 18 percent of the area, and it wears the same skin as the selection toolbar: white, 8px, the same three shadows. The rule this settles is that anything docked stays dark and anything floating over the content is a light panel, which is also what the control panel next door does. The field name moved above the control as a small caps line, so the panel is no longer wide enough to cover the row it serves.

The Done button is gone from a single control. By the time anybody could press it the answer was already recorded and already showing on the page, and Escape and a click outside both already closed the panel without changing anything. One control, one decision, and the decision closes it. The wide markdown editor keeps its button, because a text field has no event that means "finished".

Three bugs that came out of the review, one of them real

  • An arrow key committed the wrong value and took the panel away. A closed <select> fires change on every arrow key, which is native and correct; closing on it was not. Somebody reading down the list with the keyboard got one keystroke and then a shut panel with the neighbouring value set. The panel now knows which hand answered: a pointer closes it, a keyboard waits for Enter. The old test used selectOption(), which goes straight past the keyboard path, so it stayed green through all of it.
  • The select and the date had no edge of their own. all: unset takes the border with everything else, and appearance: auto hands back the chevron but not the box. At rest the control was a word floating in white on a white card, with the focus ring as its only outline. Nothing showed it, because the panel focuses the control the moment it opens.
  • "Text · ". The separator was printed whether or not anything followed it. Fixed in the small panel first and left standing in the wide one, which is exactly the shape of mistake the small panel's comment describes.

Two checks were claiming more than they measured. "Still looks like a dropdown" only asked about appearance and passed while the border was missing; "light, like the selection toolbar" compared white to white. The first now asserts the inset edge, the second builds a real .sie-bubble and compares background, radius and shadow against it.

The fixture grew a date field. Both control branches share the two lines that decide when the panel closes, and only one of them was covered.

35 PHP tests, 127 browser checks.

1.2.0

September 22nd, 2026

Markdown is edited in place now, not in a box

The popup with the monospace source is gone by default. The text on the page becomes the editor: same heading, same measure, same font, now with a cursor in it. Type ## and it becomes a heading, - a list, **bold** bold as you close the asterisks. Select a few words and a small toolbar appears over them.

It is Tiptap, which is what Statamic's own Bard is built on, so the shortcuts are the ones clients already meet in the control panel. Fetched the first time somebody opens such a field, 180 KB over the wire, and never on a page nobody is editing. The file every editor's page does load stays at 11 KB.

The trade, stated plainly. A rich editor reads markdown into a document and writes it back out, and markdown has more than one spelling for the same document. Editing one sentence rewrites the whole field in the editor's dialect. Opening a field and closing it without typing never writes anything, because the comparison is against what the editor produced on mount rather than against what was stored. rich => false gives the old source editor back, for markdown that has to survive byte for byte.

This is the addon's first build step. One file, esbuild, committed, and a CI job that rebuilds it and fails on a diff.

The bar waits to be asked

It used to sit at the bottom of every page an editor opened, whether they were editing or reading. Now there is one small button in the corner, and Ctrl/Cmd + Shift + E does the same thing from the keyboard. Pressing it brings up the bar and switches editing on; the bar has a close button that puts both away again.

One bug found while wiring it: the handler that lets Space open a focused field was swallowing every space typed into the rich editor, because Tiptap mounts its contenteditable as a child of the marker. Typing ## produced ##, so the markdown shortcut that is the whole point never fired.

The toolbar, after Adrian saw it

Light with icons, the way Bard's is, because this is a text-formatting toolbar in a Statamic site and it should look like the one the same person meets in the control panel. Bold, italic and the heading levels are letterforms, which is what Statamic's own icons for them are too; the rest are drawn here rather than copied out of a commercial package.

And it goes in the margin beside the text where the page has one, covering nothing at all. Over the text only when there is no room, and then above the selection, because reading runs downwards. Two rounds of review had it covering first the heading above it and then the line below it; a smaller toolbar and the margin solve what moving it up and down could not.

One bug worth naming: the icons were invisible while every computed style read correctly. all: unset, which is what keeps a host site's CSS out of this addon's chrome, also resets d, and d is a real CSS property on an SVG path. Right size, right colour, no shape. It took a screenshot of the toolbar on its own to see, and there is a check for it now.

35 PHP tests, 113 browser checks.

1.1.0

September 22nd, 2026

Three more kinds of field

Version 1 could edit text. Everything else on a page rendered normally and was not clickable, which on a real client site is most of it.

  • toggle, select and date open a small control. Their value is not the text on the page: a toggle renders as whatever word the template chose, a date in whatever format it wanted. So the element becomes a trigger rather than a text box, and it needs the pair form: {{ editable field="promoted" }}{{ if promoted }}ja{{ /if }}{{ /editable }}. A select's choices come from the blueprint, and the save route checks the arriving value against them again rather than trusting the dropdown.
  • markdown opens its own source, not the rendered HTML. With a toolbar that writes the same syntax a person would type. Editing rendered output means converting it back on every save, and every such conversion loses something: the exact list marker, a reference link, a deliberate HTML block. The source round-trips byte for byte.
  • Everything else opens the control panel in an overlay. Bard, Replicator, images, grids. Bard alone is an entire editor and an asset picker is an entire browser; a second-rate copy of either is worse than one click into the real one, and the real one brings its own validation, revisions and permissions. Switch control_panel off if your control panel cannot be framed.

The three new kinds reload the page after saving, because only the server knows what the template will make of the new value. Text does not: what you typed is what is there.

A toggle travels to the server as a real boolean rather than a string, so false and "" stay different things to the blueprint.

What the review changed, again

Same loop as version 1: built, photographed on a real page, handed to a reviewer who saw only the pictures.

  • The one field version 2 was built for had no outline at all. A markdown block is a heading, paragraphs and a list; wrapped in a span it has no box, so nothing was drawn around it, and neither was the outline that says "unsaved". A markdown field now defaults to a div, and any marker that contains block content becomes one.
  • Nobody could see what they were saving in markdown. Type into a monospace box, close it, the page looks exactly as before, press Save. Closing the source editor now asks the server to render the value through its own fieldtype and puts that on the page. The one rule this addon exists to keep is that nobody saves something they have not looked at.
  • A select looked like a text field. The reset that protects the bar from the host site's CSS also strips a dropdown of its chevron and a date input of its calendar button. Handed back explicitly.
  • A flipped toggle looked exactly like an untouched one until the save and the reload. The field now shows what it will become, next to what it still says.
  • The toolbar was five unlabelled symbols. It is five words.
  • On a phone the only state was a bare digit. The count moved onto the Save button, where it has a word next to it saying what it counts.
  • The control panel overlay scrolls to the field that was double-clicked and outlines it, instead of opening the whole form at the top.

Tried and reverted in the same round: an always-visible badge naming each field's kind. On a row of four fields it lands on the neighbour's value, whichever side it is placed. It shows on hover, and the control that opens names its own kind in its header, which is what a phone gets.

35 PHP tests, 86 browser checks.

1.0.0

September 22nd, 2026

The first version

Edit text, textarea and integer fields on the live page. Mark a field with {{ editable:handle }}, double-click it as a signed-in editor, type, save.

  • {{ editable:handle }} and {{ editable field="handle" }}, with tag= for the wrapper element. Renders the plain value and nothing else for anyone who is not editing.
  • A save route at /!/statamic-inline-edit/save, behind the site's session and CSRF, checking the entry policy, the blueprint, the fieldtype list, a length ceiling and the blueprint's own validation on every request.
  • Conflict detection: a save from a page older than the entry is refused with a 409 rather than overwriting whoever saved first.
  • The editor is injected automatically on pages that rendered a marker, and those pages are marked X-Statamic-Uncacheable so an editor's version is never stored in the static cache.
  • {{ inline_edit:assets }} to place it by hand instead, for sites with a Content Security Policy.
  • English and German.

Refused on purpose, with a message rather than silently: the slug, collections with revisions enabled, any fieldtype not on the list, and any handle the page never offered.

Tested with 20 PHP tests for the tag and the save route, and 55 browser checks against the shipped script.

What a round of review changed

The editor was built, photographed on a real Statamic page, and handed to a reviewer who saw only the pictures. Three passes:

  • The bar was a floating pill. It covered whatever the site had at the bottom, and it grew with its own contents, so the Save button moved between one click and the next. It is now a band docked across the width that reserves matching space at the end of the document and publishes its height as --sie-bar-height for anything else pinned down there.
  • There was no way into a field on a phone. dblclick is a mouse gesture; on a touch screen a double tap is zoom. A single tap now opens a field while edit mode is on, targets are 44px, and the change count shortens to fit the narrow bar.
  • The primary action was green. In Statamic green means "that worked". It is blue now, and green is left to the saved message.
  • The empty placeholder inherited the page's typography and said only "Empty". It is its own chip now, with its own font, carrying the field's name from the blueprint.
  • The on state was unreadable, because the generic hover rule outranked it and the pointer is on the button the instant it is switched on. The label also changed, which read like an invitation and moved the button under the cursor. One label now, state on a dot, the colour and aria-pressed.
  • Save and Discard sat there disabled with editing off. They are absent instead.
  • The page re-wrapped when edit mode came on. Multiline fields got white-space: pre-wrap with the toggle, so paragraphs moved under the person who had just clicked. The server now flags only the fields whose stored value really contains a line break, and the style is on the element from the first paint. A textarea holding one paragraph renders exactly as a visitor sees it; nothing moves when the toggle is pressed.

One bug worth naming because it was found before release and would have been hard to see afterwards: switching edit mode on gives multiline fields white-space: pre-wrap, which makes the stored line breaks visible and changes what the browser reports as the text. Every textarea on the page therefore counted as changed before anyone touched anything, and a Save would have posted fields nobody edited. The baseline is now taken after the mode is switched, not at page load.