Skip to content
PortBay

Edit .vue pages in a running Nuxt app, nothing installed

nuxt dev accepts no config path, so PortBay extends your app with a generated Nuxt layer instead — one that contributes a source-location plugin and leaves your own config alone. Nothing to install. Pro, early access, opt-in.

The short answer

How do I edit a Nuxt page visually without installing anything?

Editing a Nuxt page visually needs a rendered element to carry a coordinate back to the .vue file behind it, and that means getting a plugin into Nuxt's Vite pipeline. Nuxt makes this awkward: nuxt dev has no --config flag at all, so a config file cannot simply be handed to it. PortBay uses the flag Nuxt does have, --extends, which takes a layer whose own nuxt.config is merged underneath your app's. The layer contributes one plugin under vite.plugins and lives in PortBay's data directory, so nothing is added to your repo and nothing of yours is replaced — Nuxt merges layers by concatenating arrays, so your own plugins keep working. Nuxt renders on the server, and the coordinates arrive in the served HTML. The editor is Pro, in early access behind a Settings opt-in, and absent from the open-source build.

Verified against the product, 2026-09-05.

In the app

A Nuxt app, extended by a layer

acme-shop · Board6 cards3 agents online
Pending2
infra
Rate-limit the API
P1
Dark mode polish
P2
Todo2
auth
Add OAuth login
P0Claude Code0/3oauth-login
Auto-dispatching
implement OAuth on app.test
db
Migrate to Postgres
P1
In Progress1
Fix flaky checkout test
P0
Done2
Set up Mailpit inbox
P2
Seed demo data
P2
1/5 · AssignA card lands in Todo and @claude is mentioned

PortBay runs your Nuxt 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 page, not a multi-frame design canvas.

Walkthrough

Your first Nuxt page edit

Four steps, none of which touches your dependencies. The layer is created before the dev server starts and deleted with nothing of yours attached.

  1. Install PortBay and add your Nuxt project
  2. Turn the visual editor on
  3. Start the project — the layer goes on automatically
  4. Open Edit in browser and change something

Install PortBay and add your Nuxt project

Homebrew installs the app and the bundled portbay CLI. Add the project folder and PortBay recognises Nuxt from your package.json, runs its dev server, and serves it at its own .test hostname over trusted HTTPS.

brew tap portbay-app/portbay && brew install --cask portbay

Turn 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 Access

Start the project — the layer goes on automatically

PortBay writes a tiny Nuxt layer into its own data directory and appends --extends to your dev command. The layer contributes one Vite plugin and pins the plugin's root to your project, which matters on Nuxt 4 because Nuxt points Vite at srcDir rather than the project root. Your nuxt.config is loaded by Nuxt itself and never rewritten.

portbay start myapp

Open 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 it into the .vue file. Your Tailwind design tokens are editable from the same rail, and renaming one rewrites every class that references it across your markup in one save.

https://myapp.test
Without PortBay

The seam Nuxt gives you, and the one it doesn't

Every other framework on this lane takes a config path. Nuxt does not, which is why it has an injection family of its own — and why the mechanism is worth seeing rather than trusting.

Measured on served HTML rather than inferred from the config: the same project without the flag returns 1677 bytes and zero stamps, with it 1914 bytes and five real coordinates.

Questions

Asked before downloading

No. The plugin ships inside the PortBay app, is written to PortBay's own data directory, and reaches Nuxt through a generated layer passed with --extends. Nothing is added to package.json, node_modules or nuxt.config, and removing PortBay leaves all three byte-identical. There is no npm package for this, because nothing needs to serve one.

No, and this was checked rather than assumed, because a generated config being silently ignored is a failure this lane has produced before. Nuxt merges layer config with defu, which concatenates arrays instead of replacing them. Re-run with a logging plugin in the project's own nuxt.config.vite.plugins and PortBay's in the layer, the resolved plugin list contained both and every stamp was still present.

Nuxt 4's app/ directory would break them, if the plugin were left to guess. Nuxt 4 points Vite at srcDir, which is <project>/app rather than the project root, so a plugin falling back to Vite's own root produces coordinates like app.vue:2:3 where the real file is app/app.vue. PortBay's layer passes the project root explicitly for exactly this reason. That was measured by removing the option and watching every coordinate lose its app/ prefix while still looking plausible.

Yes, and that is the point of measuring it on served HTML rather than in a client bundle. Nuxt renders on the server, and the source coordinates come back in the HTML the browser receives — verified with two runs of the same project differing only in the flag: 1677 bytes and zero coordinates without it, 1914 bytes and five with it, naming real files and lines in app/ and app/components/.

Yes, through the Page settings panel, which reads and writes the useHead() call in that page's own <script setup>. Because useHead() is per-component, the values genuinely belong to that route and nothing else — so a title written there cannot leak onto another page. If the route has no useHead() call yet, one is created rather than the value being written somewhere it would not take effect.

Because the minimal scaffold's app.vue is a single <NuxtWelcome /> tag, and that component's markup lives in node_modules/nuxt/dist. PortBay refuses to hand you a coordinate pointing into a dependency, so a completely untouched scaffold stamps nothing — not because the lane failed, but because the project contains no markup of its own yet. Add one real page and the coordinates appear.

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 at all. It also refuses to run while the project is shared over a public tunnel, and the edit bar only works on local .test hosts. Running your Nuxt dev server on a real .test domain with trusted HTTPS is in the free, open-source app.

Yes. Vue is one of the four formats where that structural edit is supported, so an element in a .vue page can be moved into its own component file, with the import added and the original span replaced by the new tag, in one save. It refuses by name in one case: a span carrying a template expression is not extracted, because the expression's page-local values would not exist in the new file and the result would be markup that looks right and does not compile.

PortBay mascot — a friendly blue tugboat

Give your projects and your agents a real local home.

Download for macOS

Free & open source · macOS 11+ on Apple Silicon · Pro from $10/mo