All but React Native are in beta.
All but React Native are in beta.

Beta. Buoy runs in Solid 1.9 apps built with Vite. Solid 2 is not supported yet.
Let your coding agent install Buoy:
To do it by hand, use these four steps.
Run this in your app folder:
npm install --save-dev @buoy-gg/core @buoy-gg/solid @buoy-gg/networknpm install --save-dev @buoy-gg/core @buoy-gg/solid @buoy-gg/network
npm install --save-dev @buoy-gg/core @buoy-gg/solid @buoy-gg/network
Add more tools the same way, such as @buoy-gg/storage.
Keep all Buoy packages on the same release.
Buoy brings its own React for its own screens.
Your app stays all Solid.
// vite.config.ts
import { defineConfig } from 'vite';
import solid from 'vite-plugin-solid';
import { buoy } from '@buoy-gg/core/vite';
export default defineConfig({
plugins: [solid(), buoy()],
});
// vite.config.ts
import { defineConfig } from 'vite';
import solid from 'vite-plugin-solid';
import { buoy } from '@buoy-gg/core/vite';
export default defineConfig({
plugins: [solid(), buoy()],
});
The plugin finds the tools you installed.
It adds Buoy's early hook to index.html.
It links Solid Router and Solid Query when you have them.
It leaves Buoy out of prod builds.
Put it in your router's root, inside QueryClientProvider:
import { Router, Route } from '@solidjs/router';
import { QueryClientProvider } from '@tanstack/solid-query';
import { Buoy } from '@buoy-gg/solid';
function Layout(props) {
return (
<>
{props.children}
<Buoy />
</>
);
}
render(() => (
<QueryClientProvider client={queryClient}>
<Router root={Layout}>
<Route path="/" component={Home} />
</Router>
</QueryClientProvider>
), document.getElementById('root')!);
import { Router, Route } from '@solidjs/router';
import { QueryClientProvider } from '@tanstack/solid-query';
import { Buoy } from '@buoy-gg/solid';
function Layout(props) {
return (
<>
{props.children}
<Buoy />
</>
);
}
render(() => (
<QueryClientProvider client={queryClient}>
<Router root={Layout}>
<Route path="/" component={Home} />
</Router>
</QueryClientProvider>
), document.getElementById('root')!);
<Buoy /> shows nothing in your page.
It starts Buoy in dev builds, after the first render.
It stops Buoy when it goes away, so hot reload is safe.
You can put it outside the router too.
Then Routes shows plain URLs only.
npx buoy login
npx buoy login
Sign in with a Free or Pro account.
The CLI writes VITE_BUOY_KEY to a file that Git ignores.
<Buoy /> reads it for you, so you add no key to your code.
Run your app with npm run dev.
Open Buoy's dial, then open Network.
Make a web call in your app and check the list.
| Tool | In Solid |
|---|---|
| Network, Storage, Console | Yes. They watch the browser itself. |
| Routes | Yes. Inside <Router> it shows route names and params. |
| Query | Yes, with Solid Query. See below. |
| Zustand | Yes, for vanilla Zustand stores and Solid stores. |
| Highlight | Yes. It boxes the parts that changed. |
| Redux | Yes, for Redux Toolkit. Load @buoy-gg/redux/web before you make the store. |
| Jotai | Yes, for jotai/vanilla atoms. Pass them to watchAtoms. |
| Clock, Lifecycle, Location, Permissions | Yes. They work in a plain browser. |
| Env, Events, Perf Monitor, Images, Impersonate, Time Machine | Yes. |
Inside <Router>, Routes shows each route's name and params.
Each page change shows up once.
Name a route with info:
<Route path="/pokemon/:id" component={Detail} info={{ name: 'Pokémon' }} />
<Route path="/pokemon/:id" component={Detail} info={{ name: 'Pokémon' }} />
To list every page in the sitemap, pass your routes:
<Buoy routes={routes} />
<Buoy routes={routes} />
Without it, pages join the list as you visit them.
The Go button and the MCP navigate tool use Solid Router.
Install the Query tool and React Query too:
npm install --save-dev @buoy-gg/react-query @tanstack/react-query
npm install --save-dev @buoy-gg/react-query @tanstack/react-query
Use the same version as your @tanstack/solid-query.
Both must share one copy of @tanstack/query-core.
<Buoy /> finds your client from QueryClientProvider.
Pass them to Buoy's Zustand tool:
import { buoySolidSignal, buoySolidStore } from '@buoy-gg/solid/store';
<Buoy zustandStores={{
todos: buoySolidStore([todos, setTodos]),
count: buoySolidSignal([count, setCount]),
}} />
import { buoySolidSignal, buoySolidStore } from '@buoy-gg/solid/store';
<Buoy zustandStores={{
todos: buoySolidStore([todos, setTodos]),
count: buoySolidSignal([count, setCount]),
}} />
You can read and edit them in the tool.
An edit keeps the fields you did not change.
A signal shows up as { value }.
Vanilla Zustand stores go in the same list as they are.
Highlight boxes the parts of your page that changed.
It names the nearest component you wrote.
It skips Solid's own Show, For and Switch.
It reads Solid's dev hooks, so it works in dev builds only.
It only wraps your app's code while Highlight is on.
It works next to solid-devtools. Buoy keeps the dev hooks solid-devtools sets, in either order.
Put app settings for Buoy in their own file.
Load it with load, behind a dev check:
const load = import.meta.env.DEV
? () => import('./buoy-settings').then((m) => m.settings)
: undefined;
<Buoy load={load} />
const load = import.meta.env.DEV
? () => import('./buoy-settings').then((m) => m.settings)
: undefined;
<Buoy load={load} />
The dev check lets the prod build drop that file.
To keep Buoy in a QA build, set both of these:
buoy({ enableInRelease: true })
buoy({ enableInRelease: true })
<Buoy enableInRelease />
<Buoy enableInRelease />
Account and plan rules still apply.
<Buoy /> still runs, but it loads no tools by itself.
Pass them with modules, and pass queryClient too.
Put this line first in your entry file:
import '@buoy-gg/solid/register';
import '@buoy-gg/solid/register';
Routes then shows plain URLs only.
Your build must also drop Buoy from prod.
Resolve the buoy-disabled export condition there.
Buoy runs in SolidStart 2.
Add buoy() after solidStart() in your Vite config.
SolidStart has no index.html, so the plugin can't add the hook.
Put the hook first in entry-client.tsx.
Load tools like Redux before the client:
// src/entry-client.tsx
import '@buoy-gg/solid/register';
if (import.meta.env.DEV) {
await import('@buoy-gg/redux/web');
}
const { mount, StartClient } = await import('@solidjs/start/client');
mount(() => <StartClient />, document.getElementById('app')!);
// src/entry-client.tsx
import '@buoy-gg/solid/register';
if (import.meta.env.DEV) {
await import('@buoy-gg/redux/web');
}
const { mount, StartClient } = await import('@solidjs/start/client');
mount(() => <StartClient />, document.getElementById('app')!);
Import the client last. It loads your app, and your app makes the store. If the client loads first, Redux can't see the store.
The types in @buoy-gg/solid need no React types.
Your app type-checks as it is.
Code that imports @buoy-gg/core or a tool's /web entry is different.
Those types name React types your app does not have.
For that code, set skipLibCheck: true in tsconfig.json.