Edit the template block of a .vue file from the browser
PortBay reads the template block of every single-file component and gives each element its file, line and column. Click it in the browser, change it, and the edit lands in that exact span of the .vue. Your script and style blocks are never touched. Pro, early access, opt-in.
How do I edit a .vue file visually?
Most visual editors sold to Vue teams edit content, not components — they connect to a CMS or a data file, render your app around it, and let you change the words inside a slot. That is a different job from opening the single-file component and changing its markup, which is what people usually mean. PortBay does the second one. It stamps only the template block of each .vue file, so every element in it carries its file, line and column, and your script setup and style blocks are never scanned at all. Click an element in the running app, change its text, classes or attributes, and the change is spliced into that exact byte range of the SFC, with your formatting intact. The coordinate exists only in the HTML the browser receives; it is never written back to a file. Pro, early access, opt-in under Settings.
Verified against the product, 2026-09-05.
A Vue SFC, template block only
PortBay runs your Vue app 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 page at one breakpoint, not a multi-frame design canvas.
Your first SFC edit
Four steps from a Vue project to a change committed in a .vue file. None of them adds a dependency.
- Install PortBay and add your Vue project
- Turn the visual editor on
- Start the project — the templates are already mapped
- Open Edit in browser and change a component
Install PortBay and add your Vue project
Homebrew installs the app and the bundled portbay CLI. Add the project folder; PortBay reads your package.json, recognises Vue on Vite, 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 contain the editor at all — it is not compiled in.
Settings › Account › Early AccessStart the project — the templates are already mapped
PortBay writes a vite.config.mjs into its own data directory that imports and spreads your config and adds its plugin, then starts the dev server with one --config flag. Nothing is written inside your project. The plugin applies while serving only, so vite build never loads it and no coordinate can reach a production bundle.
portbay start myappOpen Edit in browser and change a component
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. Lifting a chunk of a template out into its own .vue file is a single edit here — Vue is one of the four stacks where extraction is supported.
https://myapp.testWhat the stamper reads in a .vue file, and what it ignores
A single-file component is three languages in one file, and treating it as one blob is how a tool ends up putting an attribute inside a style rule. PortBay carves the template out first and never looks anywhere else.
The usage line is the one worth noticing. Because a .vue template is markup, a component tag carries its own coordinate — so an element that PriceCard rendered still traces back to where you used it. A .jsx or .tsx file does not get that tier, and the React page says why.
<!-- src/App.vue --><script setup> // never scanned. Not markup, so nothing here can be // mistaken for a tag and nothing here is ever rewritten. import PriceCard from "./PriceCard.vue"; const plans = [{ name: "Pro", price: "12" }];</script> <template> <main class="wrap"> <!-- stamped --> <h1 class="title">Plans</h1> <!-- stamped --> <p>Billed monthly. Cancel anytime.</p><!-- stamped, text writes --> <p>{{ plans.length }} plans</p> <!-- refused: an expression --> <PriceCard :plan="plans[0]" /> <!-- stamped: the usage tier --> <img :src="hero" alt="Hero"> <!-- src refused: bound --> </main></template> <style scoped> /* never scanned, and never written to. */ .title { font-size: 2rem; }</style>
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 →CLI
brew install --cask portbay ships a full CLI over the same Rust core: lifecycle, logs, doctor, config-as-code and the board, with --json.
Explore →Asked before downloading
Only the template blocks. The scanner walks the file, finds each template element, and marks its interior as the one region that may contain markup. A script or script setup block is JavaScript and a style block is CSS, and neither is ever scanned for elements or written to. That is why a class name that also appears inside a style rule cannot be confused for a class attribute, and why nothing PortBay does can reach your component logic.
Yes, and this is the concrete advantage of an SFC over a JSX file. In markup, a capitalised or dotted tag is a component and nothing else, so the scanner can stamp the usage site safely. That means an element rendered by a child component still traces back to the line where you used it. In a .jsx or .tsx file the same tag shape can be a TypeScript generic parameter list, so that tier is dropped rather than guessed at — a real difference between the two lanes, and the reason a Vue project maps essentially everything you can click.
Bound attributes are refused by name, not silently skipped. If you drag a new image onto an img whose src is written as :src, v-bind:src or a shorthand from another framework, PortBay tells you the src is bound dynamically and that the URL in the source is not the URL the browser will request. Writing a literal over the binding would break the binding; writing to the bound expression would need to know where its value came from. Saying so is the finished answer, and the change belongs in the data.
No, and it says which rule it hit: a text run holding braces is a template expression, so PortBay refuses it and points you at the source rather than rewriting bytes that hold a binding. Static copy in the same template edits normally. There is a related detail worth knowing in the other direction — when PortBay has to write a literal pair of braces into a Vue template, it writes them as HTML character references, because Vue's tokenizer scans the raw source bytes for its delimiter and never decodes an entity first. The browser decodes it and the visitor sees the braces.
Not into your style scoped block. A style change becomes a Tailwind class rewrite when the project uses Tailwind and the utility would genuinely win the property — the editor checks that first, because adding a class that a more specific rule in your own CSS already outranks would change nothing on screen. When it would not win, the change goes to a PortBay override sheet and you get a notice saying so. The one thing that never happens is a change reported as saved that renders no differently. Design tokens are a separate and stronger lane: renaming a Tailwind token in your v3 config or a v4 @theme block rewrites every class reference to it across your markup in one save, spliced byte-for-byte so the rest of the file's formatting is untouched.
Nothing to install for Vue on Vite. The stamping plugin lives inside the PortBay binary, is written out to PortBay's own data directory, and reaches your dev server through a config generated outside your repository — uninstall PortBay and your package.json, lockfile and node_modules are byte-for-byte what they were. Nuxt is supported through its own injection path rather than this one, because nuxt dev takes no config flag, so PortBay extends it as a layer instead. Same plugin, same stamps, different seam.
PortBay's Vue visual editor is not a CMS. It edits the .vue files in your repository, which is the opposite end of the problem from the visual-editing products built around a content backend — those are good at letting an editor change words inside a slot and explicit that they do not open the components themselves. On price: the editor is a Pro feature, in early access behind an opt-in toggle in Settings, and it is not compiled into the open-source build. It attaches only to local .test hosts and refuses while a project is shared over a public tunnel. Everything else on this page — the hostname, the HTTPS, the dev server, the task board — is in the free 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