Setup
Install the plugin, add it to Vite or Next.js, and learn the binding.
npm install -D @stevenmckinnon/xrayVite
import xray from '@stevenmckinnon/xray';
export default defineConfig({
plugins: [xray(), react()],
});That is the whole setup. The plugin is apply: 'serve', so it does not exist in a
production build.
Next.js
Next has no hook for injecting a script into the document the way Vite's
transformIndexHtml does, so this takes two pieces.
import { withXray } from '@stevenmckinnon/xray/next';
export default withXray({
// your config
});import { Xray } from '@stevenmckinnon/xray/next/client';
// ...once, inside <body>:
<Xray />;withXray wires up the source transform, so findings link back to the line of JSX
that produced them. <Xray /> boots the overlay and renders nothing. Options go on
the component: <Xray hotkey="alt+f8" />.
Verified on Next 16.3 with both dev bundlers — Turbopack, which is the default,
and next dev --webpack. Requires Next 15 or later.
Automatic injection was possible on one bundler only
Rewriting config.entry would inject the client on webpack, but Turbopack has no
equivalent — and an integration that silently does nothing on half of installs is
worse than one line in a layout file.
Neither integration ships to production
The Vite plugin is apply: 'serve'. withXray returns your config untouched outside
development. <Xray /> loads the client through a dynamic import inside a NODE_ENV
guard, and because that condition holds nothing a bundler cannot fold, the import is
never walked for dependencies and the 86kB behind it is not emitted at all.
This is checked rather than asserted. playground-next is built on both bundlers in
CI and the output is searched for strings that only the client contains — the property
is otherwise invisible, because an unfetched chunk changes nothing you can observe
from the page.
There is no `force` option any more
It used to exist, for running the overlay on a deployed site. A prop is a runtime
value, so the guard could no longer fold and the client was emitted as a 48kB lazy
chunk into every production build that rendered <Xray /> — never fetched, but
shipped. To run the overlay on a deployed site, serve dist/client.js yourself and
call __xrayClient.start(). This site does that,
which is why no option is needed for it.
The hotkey
⇧⌘X toggles the overlay — Ctrl+Shift+X off Apple. Hover to inspect, click to
pin, Esc to release.
The dev server prints the binding on start, so you never have to guess it:
➜ Local: http://localhost:5173/
➜ xray: ⇧⌘XBindings are parsed when the server starts, so a typo is an error you can see rather than a key that quietly does nothing.
xray({ hotkey: 'alt+f8' });
xray({ hotkey: ['mod+shift+x', 'shift shift'] }); // several at once
xray({ hotkey: false }); // console only, via __xray.start()A chord matches modifier state exactly, so it will not swallow a longer
combination. shift shift means tap the modifier twice, and only counts when the
key is tapped alone — holding shift to type capitals never triggers it. Function
keys may be used bare; letters may not.