A visual editor for Vite with nothing to install
PortBay launches your Vite dev server, so it adds source-location stamping to its own launch from a config generated outside your repo. Vue, Svelte, React and Qwik. Your package.json and node_modules stay byte-identical. Pro, early access, opt-in.
How do I set up visual editing on a Vite project?
Visual editing on a Vite project normally begins with installing a plugin, which means changing your package.json, your lockfile and your config to get a development-only feature. That is avoidable when whatever launches the dev server can pass configuration in from outside the repository. PortBay launches yours, so it hands Vite a generated config that imports and spreads your own and adds a source-location plugin carried inside the app itself — nothing to install, and your project byte-identical before and after. It covers Vue, Svelte, React, Preact, Solid and Qwik, stamping .vue, .svelte, .jsx and .tsx with the file, line and column behind each element. The plugin applies only while serving, so a production build never loads it. Pro, early access, opt-in under Settings.
Verified against the product, 2026-09-05.
A Vite dev server, stamped at launch
PortBay runs your Vite project at its own .test URL with trusted HTTPS and dispatches board cards into it. The visual editor edits that same running app in your real browser — one live site, not a multi-frame design canvas.
Your first edit, with nothing installed
Four steps, and none of them is an install command. The stamping is already on by the time your dev server boots.
- Install PortBay and add your Vite project
- Turn the visual editor on
- Start the project — stamping is already there
- Open Edit in browser and change something
Install PortBay and add your Vite project
Homebrew installs the app and the bundled portbay CLI. Add the project folder and PortBay detects the framework and the bundler, runs your dev server, and serves it at its own .test hostname over trusted HTTPS.
brew tap portbay-app/portbay && brew install --cask portbayTurn the visual editor on
It ships to Pro under early access and is off by default. Sign in to Pro, then flip the Early Access toggle. The open-source build does not include the editor at all — it is not compiled in.
Settings › Account › Early AccessStart the project — stamping is already there
PortBay writes a vite.config.mjs outside your repo that imports and spreads your own config, adds its plugin, and points the dev server at it with a single --config flag. Nothing is written inside your project. The plugin runs only while serving, so vite build never loads it and no stamp can reach a production bundle.
portbay start myappOpen Edit in browser and change something
The project's Visual editing section has one button. It opens your normal browser at your normal .test URL with the edit bar docked at the top, booting in Preview mode where the page stays fully interactive. Switch to Edit, double-click text to type into the live page, then Save to write to source.
https://myapp.testWhere .vue and .svelte differ from .jsx and .tsx
Both lanes are stamped, and both write back. They differ on one tier, and it is worth knowing which side of the line your project sits on before you judge the results.
So a Vue or Svelte project maps essentially everything you can click. A React project maps everything you wrote, and stops at markup that came out of a dependency you did not author.
# markup-first — .vue, .svelte, .astro, .html# host elements stamped# component usages stamped <- <Card/> carries a coordinate too# so: a library element rendered by <Image/> maps to your usage # script-first — .jsx, .tsx# host elements stamped# component usages NOT stamped# because <Card/> and <T,>(x: T) => x are the same shape to a# scanner, and a wrong insertion is a syntax error in your module # neither lane stamps:# .mdx top level is Markdown, not markup# bare .js / .ts the extension is no evidence of markup# <my-card/> a custom element would take the attribute# as a prop and clobber the child's real stamp
The pieces underneath
Visual editor
Click an element on your running site in your own browser and edit its text, classes and styles straight into the source file, across 21 stacks.
Explore →HTTPS + .test domains
https://name.test per project: mkcert certificates, managed local DNS and a bundled Caddy edge, with expiry and reissue in view.
Explore →Asked before downloading
Correct, for Vite. The plugin is compiled into the PortBay binary, written out to PortBay's own data directory, and handed to your dev server through a generated config that lives outside your repo. There is no package to add and none to remove: uninstall PortBay and your package.json, node_modules and config files are byte-for-byte what they were. The plugin is not on npm and never will be, because nothing needs to serve it.
Vue, Svelte and SvelteKit, React (which is also the lane Preact and Solid take), Qwik and Angular — anything running on Vite. Astro and Nuxt are supported too, through their own injection paths, because neither takes the same config flag: Astro needs a project-relative config path and Nuxt has no --config at all, so it is extended as a layer instead. Each of those has its own page with the details specific to it; this page covers the mechanism they share.
JSX was refused, and that changed in August 2026. Documentation and older write-ups still say .jsx and .tsx cannot be stamped; that is out of date. Both are scanned script-first now and their host elements carry data-pb-loc, and a className edit writes back through a tree-sitter JSX path. One tier genuinely did not come back — see the next answer.
Component usages. In a .vue or .svelte file, a <Card/> tag carries its own coordinate, so an element a component rendered still traces back to your markup. In a .jsx or .tsx file it does not, because a capitalised tag there can be a TypeScript generic parameter list rather than a component, and inserting an attribute into the wrong one is a syntax error in your module. The practical loss is markup produced by a library you did not author: it carries no coordinate.
No. The plugin declares apply: 'serve', so Vite loads it for the dev server and never for vite build. There is also nothing to leave behind in your source, because the attribute is added during serving rather than written into your files — the difference from editors that write their own id attributes into your components and expect you to strip them later.
No. PortBay can only add its own instrumentation to dev servers it launches through Vite or Astro, so Babel pipelines get no stamping. Text, class and attribute edits still resolve by searching your source, but structural edits are refused rather than guessed. If you are on Next.js, that has its own supported path through an SWC plugin.
Two capabilities are worth knowing about. Renaming a Tailwind design token rewrites every reference to it across the project in the same save, so the definition and its uses never drift apart. And a selection can be extracted into a new component, which is supported on Astro, JSX, Svelte and Vue — the four languages whose grammar PortBay can move a subtree in safely. It refuses by name when a selection contains a template expression it cannot move rather than producing something that does not compile.
No. It is a Pro feature, still in early access behind an opt-in toggle in Settings, and it is not compiled into the open-source build. The rest of what this page describes — running your Vite dev server, the .test hostname, trusted HTTPS, the task board — is in the free, open-source app.

Give your projects and your agents a real local home.
Download for macOSFree & open source · macOS 11+ on Apple Silicon · Pro from $10/mo