A floating admin bar that lets you edit any text on your website in place — no CMS migration, no code changes, works on any frontend stack.
Self-hosted, open-source alternative to Weglot's in-context editor or a Storyblok visual editor — for the much narrower job of "let someone fix text on an existing site." Works as an inline text editor for React, Vue, Astro, WordPress, or plain HTML, with no CMS migration.
Drop one <script> tag into any customer-facing site — a marketing page, a
client site, an internal tool — and give whoever owns the content a way to
fix a typo, update a CTA, or tweak copy themselves, without opening a pull
request or waiting on a developer.
<span data-edit-id="hero.title">Welcome to Acme Studio</span>
...
<script src="https://your-editbar-server.example.com/widget.js" defer></script>That's the entire integration. data-edit-id is a plain HTML attribute, so
it survives being rendered by React, Vue, Svelte, Astro, a static site
generator, a CMS template, or hand-written HTML — the widget doesn't care.
A small, unobtrusive bar sits bottom-left, visible only to signed-in admins:
┌─────────────────────────────────────────────┐
│ ● Edit Save changes Discard ✕ │
└─────────────────────────────────────────────┘
- Edit — click any highlighted text on the page and rewrite it in place.
- Changes are cached locally until you explicitly Save changes — nothing is public until you say so.
- Discard reverts to the last published version.
- ✕ collapses the bar to a small pencil tab hugging the left edge —
click it to bring the full bar back. Visiting the page with
#editbarin the URL always force-opens it, even if it was left collapsed, without needing to retype the token. - ⚙ opens a Settings panel with the current admin token (masked by default), one-click copy of the token or a ready-to-share admin link, and a Rotate token button that invalidates every other copy of the link immediately.
- Regular visitors never see the bar and never touch anything but the published text.
git clone <this repo>
cd editbar
npm install
npm run devNo EDIT_TOKEN needed — on first run the server generates a strong random
admin token itself, saves it to packages/server/data/token.txt, and prints
it to the console along with a ready-to-use admin URL. Reuse the same
command later and it picks the saved token back up.
The server listens on http://localhost:4000 and serves:
GET /widget.js— the widget scriptGET /overrides.json— the current published text (public)GET /config— plan/feature flags the widget can branch on ({ plan, features })GET /setup— a one-time setup page showing the token and embed snippet (see "Provisioning the admin token" below)POST /overrides— save changes (requiresAuthorization: Bearer <token>, rate-limited, capped payload size)POST /token/rotate— issue a new token, invalidating the old one (requires the current token)GET /demo— a working vanilla HTML example
Open /demo without a token to see the page exactly as any visitor sees it
(no bar); the console output from npm run dev gives you the admin URL to
open instead. If you'd rather set the token yourself, EDIT_TOKEN=choose-a-real-secret npm run dev
skips auto-provisioning entirely (and disables the rotate endpoint, since
the environment variable is then the source of truth).
- First run: the console prints the generated token and a link to
/setup— a small page with the token (copy button) and the exact embed snippet. For remote/non-localhost visitors this page is one-time: once viewed (or after 15 minutes, whichever comes first) it stops showing the token and points back to the console output instead. Requests from localhost can always reach it — handy while you're still setting things up directly on the machine running the server. - Later: once you've activated the bar once with
?edit_token=..., the Settings panel (⚙) is the ongoing way to see, copy, or rotate the token — no need to go back to/setupor the console.
The same widget, dropped in unmodified, also runs in:
examples/react-app—npm run dev:reactexamples/vue-app—npm run dev:vueexamples/astro-app—npm run dev:astro
Each proves the same point: data-edit-id is a plain HTML attribute, so it
survives whatever compiles or server-renders the page.
Run npm test to run the unit and integration test suite (Vitest +
Supertest) covering packages/core and packages/server.
There's no plugin (yet) — paste the same two lines used everywhere else into
your theme's footer template (e.g. footer.php, or the theme's "custom
scripts" field if your theme/builder exposes one), and add data-edit-id to
whichever template tags render the text you want editable:
<h1 data-edit-id="hero.title"><?php the_title(); ?></h1>
...
<script src="https://your-editbar-server.example.com/widget.js"
data-api="https://your-editbar-server.example.com" defer></script>- On load, the widget fetches
/overrides.jsonand patches anydata-edit-idelement whose key has a published override. - If a valid admin token is found (from
?edit_token=..., cached thereafter inlocalStorage), the floating bar renders. - Edits are cached in
localStorageas drafts — nothing is sent to the server until Save changes. - Saving
POSTs the draft to/overrideswith the bearer token; the server verifies it, merges the change intooverrides.json, and from that point every visitor's page load reflects the new text.
See docs/ARCHITECTURE.md for the full request flow
and the security model (why the bar's visibility is cosmetic, not the actual
access control).
The reference server (packages/server) reads these environment variables:
| Variable | Default | Description |
|---|---|---|
PORT |
4000 |
Port the server listens on |
EDIT_TOKEN |
(auto-generated if unset) | Shared secret required to save changes. Set this yourself to disable auto-provisioning and the rotate endpoint. |
TOKEN_FILE |
packages/server/data/token.txt |
Where the auto-generated/rotated token is persisted (ignored if EDIT_TOKEN is set) |
OVERRIDES_FILE |
packages/server/data/overrides.json |
Where published text is stored |
The server is a plain Node/Express app with no external services required —
overrides.json is a flat file, so it's easy to back up, diff, or commit to
git alongside your site. Run it anywhere that can run Node (a small VM, a
container, a serverless function with persistent storage). Point the
widget's data-api attribute at wherever you deploy it:
<script src="https://your-editbar-server.example.com/widget.js"
data-api="https://your-editbar-server.example.com"
defer></script>You can also load the script itself straight from this repo via jsDelivr's
GitHub CDN, instead of serving it from your own backend — only data-api
needs to point at wherever you run the actual API (/overrides.json,
/overrides, /config):
<script src="https://cdn.jsdelivr.net/gh/jindrabe/editbar@main/packages/widget/src/widget.js"
data-api="https://your-editbar-server.example.com"
defer></script>Tools like Storyblok, Builder.io, or Weglot solve a related but bigger problem — they usually want you to build your site on top of them, or add a per-framework SDK. The closest open-source relatives are similarly broader in scope: Tolgee does in-context editing but for localization strings specifically, via a framework SDK; TinaCMS does visual editing but as a full git-based headless CMS your site has to adopt. Editbar is intentionally narrower than all of them: if all you need is "let a non-technical person fix text on an existing site," it's a five-minute integration instead of a CMS migration or an SDK install.
| Editbar (OSS) | Tolgee | TinaCMS | Typical headless CMS | |
|---|---|---|---|---|
| Integration | one <script> tag |
SDK per framework | adopt Tina's data layer | SDK per framework |
| Works on existing sites | yes | yes (for i18n strings) | requires restructuring content as Markdown/MDX/JSON | usually requires rebuilding templates |
| Scope | any text on the page | translation strings only | full content modeling | full content modeling |
| Self-hostable for free | yes | yes | yes | rarely |
Is this SEO-safe? Overrides are patched in on the client after load, so Google (which renders JavaScript) sees the edited text, but non-JS scrapers and link-preview bots see the original. A guaranteed-SEO-visible mode (server-render time resolution) is planned as a paid add-on for teams that need it — see Roadmap.
Does it work with WordPress / Webflow / Squarespace / a static site
generator? Yes — anywhere you can add a <script> tag and a data-edit-id
attribute to your markup. See "Using it with WordPress" above; the same
pattern applies to any template language.
Can visitors see or use the edit bar? No — it only renders when a valid
admin token is present, and every write is independently verified
server-side regardless of what the UI shows. See
docs/ARCHITECTURE.md for the full security model.
What can't the free version do? Rich content (images/links), multiple admins/roles, version history, and an SEO-guaranteed render mode — all planned as part of the hosted product, not artificially removed from the OSS core. See Roadmap below.
The open-source core stays focused on drop-in text editing, freely self-hostable, MIT-licensed, with no artificially crippled features. A hosted version is planned on top of the same core, in increasing tiers, for teams that want to skip running their own backend:
- Starter — managed backend/storage, real per-account login instead of a single shared token; otherwise the same feature scope as the OSS core.
- Pro — draft → publish review flow with version history and rollback; rich content types (image swap, link editing) beyond plain text; SEO-guaranteed rendering (SSR/build-time resolution) as an opt-in adapter per framework, for teams that need overrides visible to non-JS crawlers.
- Agency — a multi-site dashboard for managing several client sites from one account, with team members and roles.
- Enterprise — approval workflows (an editor submits a batch of changes, an approver reviews the diff and approves or rejects it, mirroring a pull-request review), SSO, and audit log export.
None of this is required to use the free, self-hosted core.
See CONTRIBUTING.md.
MIT — see LICENSE.