Vite plugin / Open source

Svelte
Inline Edit

Edit the words on the page. The file changes.

Press a key, click a paragraph, type. The edit is written straight back into the .svelte file it came from. No agent, no CMS, no desktop app — the compiler records where every string lives, and the dev server splices the file.

$ npm i -D vite-plugin-svelte-inline-edit
Left: the running site. Right: the source file. Nothing else is involved.

The awkward part

Rendered text has no obvious way back to the template that produced it. And at runtime these two are indistinguishable — both are one element rendering one text node:

<p>Static prose</p>                     ← a byte range in the file
<figcaption>{shot.caption}</figcaption>  ← text lives in a JS array

They need completely different writes, so guessing from the DOM would eventually splice a literal over {shot.caption} and destroy the expression. The decision has to be made where the answer actually exists: at compile time. The plugin parses every .svelte file and stamps each element it can safely edit, so the browser's rule becomes trivially correct — editable if the mark is there, inert if it isn't.

Three shapes it can write

01

Plain prose

<p>Static prose.</p>

Marked with a byte range in the file. The simple case, and the one every visual editor already handles.

02

Prose with inline tags

<p>with <strong>emphasis</strong>.</p>

The whole inner range is the unit. The browser rebuilds the markup through a strict allowlist and the server re-parses it before writing, so a bad rebuild fails loudly rather than corrupting the file.

03

Text out of an {#each}

{#each cards as c}<h3>{c.title}</h3>

The one that blocks every other tool — and usually where most of the copy actually lives. On the page this was built for, 60 of 110 editable nodes came out of arrays.

The array form deliberately carries no index — that's a runtime fact the compiler can't see. The server finds the entry by matching the text being replaced instead, which makes the write self-verifying and fails safe on duplicates.

Refusing is the whole game

Anything it can't put back faithfully is never offered. No outline, no caret, no surprise in your diff.

  • A <pre>, or anything with preserving white-space Its layout is its content, and writes collapse whitespace.
  • Prose containing an <a href> or any attributed tag The DOM cannot tell you an onclick={…} handler ever existed.
  • Text from data.items or a function call There is no literal in the file to splice.
  • Two array entries holding identical text Ambiguous. It refuses rather than guessing which one you meant.
  • Anything mixing markup with an {expression} The expression is source the DOM does not remember.

What it looks like

A page with edit mode armed, editable text outlined in dashed blue
Armed. Everything it can safely write is outlined; everything else stays quiet.
The .svelte source file with the edited line highlighted
The same edit, landed in the file. Formatted, and ready to commit.
3
editable shapes
38
tests on the rules
0
bytes in your build
~0ms
no model in the loop

Why it didn't already exist

Svelte already had the read-only half. The official inspector jumps you to the right line, and svelte-trace stamps source locations — then stops, describing itself as a foundation for other tools. Nobody built the tool.

What does write back is elsewhere: Onlook is React-only and a desktop app, stagewise routes every edit through a language model, and the CMS visual editors edit CMS content rather than your files. Splicing the source directly is faster than all of them, costs nothing, and works on a plane.

It exists because I was writing a long project page by hand and got tired of hunting for which array a caption lived in. That's still the honest use case: fixing your own copy, in place, without breaking flow.

Two lines and a keypress.

$ npm i -D vite-plugin-svelte-inline-edit

Add inlineEdit() to your Vite plugins. It's dev-only by construction — nothing in it reaches a production build.

Get it on npm
Back to All Projects