Skip to content
PortBay

Visual editor

A visual editor for your codebase, not a copy of it

Open your project's own .test URL in your own browser. PortBay injects an edit bar into the page, and the element you click resolves to a file, a line and a column you can edit in place.

21 stacksWrites to your sourceOpen source
acme-shop.test
body›header.hero›button.cta
Sign inBag0
New season
Built for the long way home.

Hard-wearing gear, quietly designed. Free shipping over $75.

Shop the drop

Harbour tote

$210Add

Trail pant

$145Add

Wool crew

$98Add
0 pending editsbase1280 × 800

Finding the element by handThe loop it replaces: guess the file, grep a string you hope is unique, edit, alt-tab back to check.

Open the project the way you always do

Start the project and visit its .test hostname in Chrome, Safari or anything else. The bundled Caddy edge routes the page through PortBay's edit proxy, which injects the bar into a shadow root. There is no app webview and nothing to open on the side.

button.cta → partials/hero.blade.php:12:9

Click an element

The bar switches from Preview to Inspect to Edit. Selecting an element resolves it to an authored coordinate — the file, line and column that produced it — through five anchor tiers, starting with the dev plugin's location stamp and falling back through a unique id, tag plus DOM ordinal, and more when it is absent.

Visual edit history

Kept 30 days

A snapshot is taken before every apply. Restoring rolls the touched files back to the state before that apply — behind a diff, like everything else.

14:02:19tx_9f3a1c

listings.blade.php · partials/hero.blade.php

13:47:55tx_7b20de

home.blade.php

11:16:04tx_41ccb8

resources/views/ — four templates

Change the thing you are looking at

Text, classes, attributes and styles, plus structural edits — insert, delete, duplicate, move. Double-click to type into the page directly. The right-hand rail carries layout, spacing, typography, borders and effects, and a Tailwind token you rename is rewritten everywhere it is referenced in the same save.

Round the listing CTA and warm its border.

Done. Two files, 23 bytes. Nothing else moved — open the diff?

What do you want to work on?
Save, and read what actually changed

The write goes through the same engine the in-app Apply uses: a surgical splice into the byte span, never a parse-and-reprint. Comment placement, quote style, trailing commas and key order outside the patched span come back byte-identical.

listings.blade.php+2−2
@@ 41,6 +41,6 @@
<a href="/drop"
- class="rounded-md border-slate-200"
+ class="rounded-md border-[var(--rope)]"
Shop the drop
- @apply text-slate-900;
+ color: var(--harbour);

Tokens

Brand

--harbour#1f3d5c
--sail#2f6fed
--rope#c8a882

Surface

--paper#f7f6f2
--slate-deep#14161c
Review the diff, or put it back

Every save sits on a shadow-git checkpoint kept outside your own history. The Diff button shows the working diff against it, and revert restores the pre-apply snapshot — per edit or all of it.

In the bar

What one click reaches

The bar is injected into your own page at your own .test URL. Each of these writes into the file that produced the element you clicked.

Point at the element

Selecting it resolves to a file, a line and a column, through five anchor tiers starting at the dev plugin's location stamp.

Rename the token once

Text, classes, attributes and styles. A Tailwind token you rename is rewritten across the project's authored markup in the same save.

Insert, delete, move

Structural edits splice into the source the same way. Page settings cover title, description, robots and JSON-LD on 19 of the 21 stacks.

...and so
much more

Your machine, start to finish
Your browserYour dev serverNo cloud
Where the code goes
Nowhere. The editor runs against the dev server already on your machine, in the browser you already had open — there is no sandbox to upload a repo to, no hosted preview, and no copy of the project on someone else's disk.
21 stacks, all four write lanes
Stacks covered
Static HTML, React, Vue, Svelte, Next.js, Astro, Angular, Remix, Nuxt, SolidStart, Qwik, Laravel, Livewire, Inertia, Rails, Django, WordPress, WordPress block themes, Hugo, Jekyll, Eleventy.
Four lanes, every stack
TextAttributeClassPage settings
Write lanes per stack
Text, attribute, class and page settings. A standing cross-stack test drives all four against all 21; a lane that cannot work on a stack has to refuse out loud, in a sentence the test checks for.
The write, in bytes
listings.blade.php:42:18
rounded-md → rounded-full
14 bytes written · nothing else moved
How the file is written
A byte splice into the authored span. No formatter runs, and no marker attribute is written into your source.
Insert, delete, duplicate, move
Structural edits
Insert, delete, duplicate and move. Extract-to-component is narrower — Astro, JSX/TSX, Svelte and Vue — and refuses spans carrying a template expression rather than emitting source that will not build.
Written into the head, not a plugin
<title>Harbour tote — Acme
og:image /og/listing.png
canonical /shop/harbour-tote
application/ld+json Product
Page settings and SEO
Title, description, robots, canonical, og:, twitter:, JSON-LD and custom head/body code. WordPress and WordPress block themes refuse this lane and say why.
Records in the same panel
PayloadSanityWordPressStrapi 5PortBay DBStrapi 4
CMS records in the same panel
Payload, Sanity, WordPress, Strapi 5 and a PortBay-managed database. Strapi 4 is refused by name.
The agent writes through the same door
portbay_editor_applydry run · 2 files, 23 bytesapplied · revertable from history
Agent write-back over MCP
portbay_editor_apply, plus working-diff, history, restore-preview and restore. A dry run first, then an exact file consent list, then a human approval.
One canvas, every width
Desktop — base breakpoint
Breakpoint canvas
One canvas at a time. There is no multi-frame infinite canvas and it is not planned — see the FAQ.

Get started

PortBay resolves the element you clicked to a file, line and column across 21 stacks, and replaces only the bytes that changed.

FAQ

Frequently asked questions

Don’t see your question answered here? The docs go deeper — every feature, flag and failure mode.

Read the docs

Three ways, and one of them favours Onlook. PortBay writes by splicing the exact byte span in your file, so authored formatting survives and no marker attribute is added to your source; Onlook writes data-oid attributes into your files and reprints them through Prettier. PortBay runs locally against your own dev server on 21 stacks including Laravel, Rails, Django and WordPress; Onlook is Next.js plus Tailwind in a cloud sandbox. In Onlook's favour: it has a multi-frame infinite canvas and PortBay does not. PortBay has one breakpoint canvas, that was a deliberate decision rather than a missing feature, and a designer who works across frames will notice immediately.

No marker lands in your files. The fastest way to identify an element is a data-pb-loc location stamp, and that is applied by the dev-server transform on the way to the browser, never written back into your source. When the stamp is not there the edit is not refused: four more anchor tiers follow it, starting with a unique id that matches exactly one opening tag across the project's authored markup, then tag plus the captured DOM ordinal.

Twenty-one, checked by a standing test rather than a README: static HTML, React, Vue, Svelte, Next.js, Astro, Angular, Remix, Nuxt, SolidStart, Qwik, Laravel, Livewire, Inertia, Rails, Django, WordPress, WordPress block themes, Hugo, Jekyll and Eleventy. The test drives four write lanes — text, attribute, class and page settings — against every one of them and reads the changed bytes back off disk, and any lane that cannot work on a stack has to refuse in a sentence the test checks for. Page settings is the one lane with real gaps: both WordPress rows refuse it.

Yes, through the same write-back engine over MCP. portbay_editor_apply is a two-step tool: the agent calls it with dryRun first and gets back the exact set of files the planner would write, which is not just the files it named, then calls again passing every one of those paths back as an exact consent list. If the list does not match the plan the call refuses and writes nothing. The real write also blocks on a human approval, and returns a snapshot id that undoes exactly that apply.

No. It ships in the app — there is no plan to buy and no opt-in to turn on. It is macOS only, and it will not run while the project is shared over a public tunnel: a project with a running tunnel is dropped from the eligible set so the edit bar and its write endpoints never answer public traffic.