Alt-Shift and click anything on your page to open a comment thread.# pnpm
pnpm add -D @ui-bridge/unplugin @ui-bridge/mcp
# npm
npm install --save-dev @ui-bridge/unplugin @ui-bridge/mcp
# yarn
yarn add -D @ui-bridge/unplugin @ui-bridge/mcp
// vite.config.ts
import { defineConfig } from 'vite';
import { uiBridgeVite } from '@ui-bridge/unplugin';
export default defineConfig({
plugins: [...uiBridgeVite()],
});
# pnpm
pnpm add -D @ui-bridge/astro @ui-bridge/mcp
# npm
npm install --save-dev @ui-bridge/astro @ui-bridge/mcp
# yarn
yarn add -D @ui-bridge/astro @ui-bridge/mcp
// astro.config.mjs
import { defineConfig } from 'astro/config';
import uiBridge from '@ui-bridge/astro';
export default defineConfig({
integrations: [uiBridge()],
});
# pnpm
pnpm add -D @ui-bridge/next @ui-bridge/mcp
# npm
npm install --save-dev @ui-bridge/next @ui-bridge/mcp
# yarn
yarn add -D @ui-bridge/next @ui-bridge/mcp
// next.config.mjs
import { withUiBridge } from '@ui-bridge/next';
export default withUiBridge({
// your Next.js config
});
# pnpm
pnpm add -D @ui-bridge/nuxt @ui-bridge/mcp
# npm
npm install --save-dev @ui-bridge/nuxt @ui-bridge/mcp
# yarn
yarn add -D @ui-bridge/nuxt @ui-bridge/mcp
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@ui-bridge/nuxt'],
});
UI Bridge exposes an MCP server — your agent reads your feedback and applies changes through it.
// vscode/mcp.json
{
"servers": {
"ui-bridge": {
"type": "stdio",
"command": "npx",
"args": ["--no", "ui-bridge-mcp"]
}
}
}
// .mcp.json (project root)
{
"mcpServers": {
"ui-bridge": {
"type": "stdio",
"command": "npx",
"args": ["--no", "ui-bridge-mcp"]
}
}
}
// ~/.cursor/mcp.json
{
"mcpServers": {
"ui-bridge": {
"type": "stdio",
"command": "npx",
"args": ["--no", "ui-bridge-mcp"]
}
}
}
Run your dev server. Alt-click any element to open a comment thread — your agent picks it up through the MCP server.
UI Bridge ships with sensible defaults and lets you override them — either as project-wide plugin config or at runtime through the preferences dialog in the browser.
Pass a preferences object to your integration to set defaults for your whole project. These are used as the base layer and can still be overridden by individual users at runtime.
// vite.config.ts
import { defineConfig } from 'vite';
import { uiBridgeVite } from '@ui-bridge/unplugin';
export default defineConfig({
plugins: [
...uiBridgeVite({
preferences: {
commentBar: { position: 'bottom-right' },
visibility: { status: 'always' },
},
}),
],
});
The same
preferenceskey is available on all integrations (withUiBridge,uiBridge(),@ui-bridge/nuxt).
Click the gear icon (⚙) in the comment bar to open the preferences dialog. Changes are saved immediately to .ui-bridge/preferences.json and broadcast to all connected browser sessions.
interface VisibilityConfig {
/**
* Which comments/knobs are visible.
* 'non-approved' hides threads whose tweaks are all accepted or discarded.
* @default 'non-approved'
*/
status?: 'always' | 'non-approved' | 'never';
/**
* URL-based filter. All false/unset means show all comments regardless of URL.
* Bar default: all false (show across all routes — click to navigate).
* Panel default: { path: true } (current page only).
*/
route?: {
/**
* Only show comments whose pageUrl matches the current origin
* (protocol + host + port).
* @default false
*/
domain?: boolean;
/**
* Only show comments whose pageUrl pathname matches the current page.
* Bar default: false. Panel default: true.
*/
path?: boolean;
/**
* Only show comments whose pageUrl query string matches the current page.
* @default false
*/
params?: boolean;
};
}
interface UserPreferences {
/**
* Shared visibility defaults for both the comment bar and the comment panel.
* Each context can override individually.
*/
visibility?: VisibilityConfig;
commentBar?: {
/**
* Where the comment bar is pinned on screen.
* @default 'top-left'
*/
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
/**
* Visibility overrides for the comment bar.
* Merged on top of `visibility`.
* Bar-specific route default: all false (show comments from all routes
* so users can click a badge to navigate to a different page).
*/
visibility?: VisibilityConfig;
};
ui?: {
/**
* Visibility overrides for the floating comment panel.
* Merged on top of `visibility`.
* Panel-specific route default: { path: true } (current page only).
*/
visibility?: VisibilityConfig;
};
}
The MCP server finds its data by looking for a .ui-bridge/ folder, starting from its working directory and walking up. This works automatically when UI Bridge runs at your project root.
If UI Bridge runs in a subdirectory — a docs/ folder, a monorepo package, etc. — set cwd to that directory:
// .vscode/mcp.json
{
"servers": {
"ui-bridge": {
"type": "stdio",
"command": "npx",
"args": ["--no", "ui-bridge-mcp"],
"cwd": "${workspaceFolder}/packages/my-app"
}
}
}
If possible, UI Bridge automatically detects to which file and even line a comment belongs using code-inspector. If your stack doesn’t support it — or you want to override it — you can read it from HTML comments or data attributes in the rendered markup.
Use htmlComments with a regex pattern. Useful for e.g. Laravel Blade with laravel-view-debug that wrap partials in comments like <!-- Start view: /path/to/file.blade.php -->. Set inspector: false when code-inspector can’t annotate your templates:
// vite.config.ts
import { defineConfig } from 'vite';
import { uiBridgeVite } from '@ui-bridge/unplugin';
export default defineConfig({
plugins: [
...uiBridgeVite({
inspector: false,
sourceAnnotation: {
// Each entry is a regex. Capture group 1 is the file path.
// List multiple patterns if you mix frameworks — first match wins.
htmlComments: [{ pattern: 'Start view: (.+?\\.blade\\.php)' }],
},
}),
],
});
Use dataAttributes if your build pipeline stamps location onto elements. Supports a single file:line:col attribute or separate file and location attributes:
// vite.config.ts
import { defineConfig } from 'vite';
import { uiBridgeVite } from '@ui-bridge/unplugin';
export default defineConfig({
plugins: [
...uiBridgeVite({
sourceAnnotation: {
// Single attribute encoding "file:line:col"
dataAttributes: [{ pathAttr: 'data-source' }],
// Or split across two attributes:
// dataAttributes: [{ fileAttr: 'data-file', locAttr: 'data-loc' }],
},
}),
],
});
UI Bridge stores everything in a .ui-bridge/ folder at your project root. Deciding what to commit — and what to ignore — is a team decision.
.ui-bridge/| Path | Contents | Commit? |
|---|---|---|
comments/*.json | One file per comment thread (selector, text, replies, knobs) | Yes — this is your shared feedback |
preferences.json | User-overridden preferences from the browser dialog | No — see below |
scripts/*.mjs | Knob transform scripts (the code that edits your source files) | Yes — needed to replay tweaks |
There are two ways to share preferences with your team. Usually it’s best to set defaults in the plugin config and let individuals adjust as they like through the browser dialog — those changes are local and won’t affect anyone else.
The simplest way to keep preferences local is to gitignore the whole file:
# .gitignore
.ui-bridge/preferences.json
Set preferences in your plugin config. These are checked into version control as part of your vite.config.ts (or equivalent) and apply to everyone:
// vite.config.ts
...uiBridgeVite({
preferences: {
commentBar: { position: 'bottom-right' },
visibility: { route: { path: true } },
},
})
If someone adjusts preferences via the browser dialog and you want to share that with the team, commit .ui-bridge/preferences.json. It overrides the plugin defaults for all users who pull it.
The two layers merge: plugin config is the base, preferences.json overrides it. Individual users can override again via the dialog — but those changes will be overwritten the next time they pull if you’re committing the file.
Comment files in comments/ are plain JSON. Commit them and every team member sees the same open threads when they run their dev server. The agent reads them through MCP and can pick up where you left off.
Knob scripts in scripts/ are the transform functions your agent writes when replying with a live tweak. They’re referenced by comment threads, so if you commit comments you should commit their scripts too — otherwise teammates won’t be able to replay the tweaks.
If you prefer to keep the .ui-bridge/ folder entirely local:
# .gitignore
.ui-bridge/
In that case, comments and tweaks are personal and not shared across the team.
MIT — see LICENSE for details.