Awesome-omni-skill react-grab
Installs and configures React Grab for visual UI element selection in React/Electron apps. Use when user wants to edit UI visually, select components by hovering, or capture element context.
git clone https://github.com/diegosouzapw/awesome-omni-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/react-grab" ~/.claude/skills/diegosouzapw-awesome-omni-skill-react-grab && rm -rf "$T"
skills/development/react-grab/SKILL.mdReact Grab Integration
Step 1: Detect Project Type
IMPORTANT: Before installing, detect the project type by checking these files:
| Check For | Project Type |
|---|---|
| electron-vite |
+ | Electron Forge + Vite |
+ | Electron Forge + Webpack |
in package.json + | Electron + Webpack |
in package.json + | Electron + Vite |
in package.json (no bundler) | Plain Electron |
| Next.js |
(no electron) | Vite |
(no electron) | Webpack |
Detection Commands:
# Check package.json for framework cat package.json | grep -E "(electron|next|vite)" # Check for config files ls -la | grep -E "(electron-vite|forge|next|vite|webpack)\.config"
Step 2: Install
# Use the project's package manager (check lockfile) pnpm add -D react-grab # if pnpm-lock.yaml exists npm install -D react-grab # if package-lock.json exists yarn add -D react-grab # if yarn.lock exists
Step 3: Configure Based on Project Type
A. Next.js
File to edit:
app/layout.tsx (App Router) or pages/_app.tsx (Pages Router)
App Router -
:app/layout.tsx
import Script from "next/script" export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html> <head> {process.env.NODE_ENV === "development" && ( <Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" /> )} </head> <body>{children}</body> </html> ) }
Pages Router -
:pages/_app.tsx
import Script from "next/script" import type { AppProps } from "next/app" export default function App({ Component, pageProps }: AppProps) { return ( <> {process.env.NODE_ENV === "development" && ( <Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" /> )} <Component {...pageProps} /> </> ) }
B. Vite (Web Only)
File to edit:
src/main.tsx or src/index.tsx
import React from "react" import ReactDOM from "react-dom/client" import App from "./App" // Add this block at the top, after imports if (import.meta.env.DEV) { import("react-grab").then(({ init }) => init()) } ReactDOM.createRoot(document.getElementById("root")!).render( <React.StrictMode> <App /> </React.StrictMode> )
C. electron-vite
Files to edit:
- Initialize react-grabsrc/renderer/src/main.tsx
- Configure CSPsrc/main/index.ts
Step C1 -
:src/renderer/src/main.tsx
// Add at the top, after imports if (import.meta.env.DEV) { import("react-grab").then(({ init }) => init()) }
Step C2 -
: Find the src/main/index.ts
BrowserWindow creation and add CSP:
import { app, BrowserWindow } from "electron" import { join } from "path" function createWindow() { const mainWindow = new BrowserWindow({ width: 1200, height: 800, webPreferences: { preload: join(__dirname, "../preload/index.js"), sandbox: false // Required for react-grab source mapping } }) // Add this block for development CSP if (process.env.NODE_ENV === "development") { mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => { callback({ responseHeaders: { ...details.responseHeaders, "Content-Security-Policy": [ "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'" ] } }) }) } // ... rest of window setup }
D. Electron Forge + Vite
Files to edit:
orsrc/renderer.ts
- Initialize react-grabsrc/renderer/index.tsx
- Configure CSPsrc/main.ts
Step D1 - Renderer entry:
// Add at the top, after imports if (import.meta.env.DEV) { import("react-grab").then(({ init }) => init()) }
Step D2 -
: Add CSP configuration (same as electron-vite Step C2)src/main.ts
E. Electron + Webpack
Files to edit:
- Initialize react-grabsrc/renderer/index.tsx
- Configure CSPsrc/main/main.ts
Step E1 -
:src/renderer/index.tsx
// Add at the top, after imports if (process.env.NODE_ENV === "development") { import("react-grab").then(({ init }) => init()) }
Step E2 - Main process: Add CSP configuration (same as electron-vite Step C2)
F. Plain Electron (No Bundler)
Files to edit:
- Add script tagindex.html
- Configure BrowserWindowmain.js
- Initialize react-grabrenderer.js
Step F1 -
:index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' unpkg.com; style-src 'self' 'unsafe-inline'" /> <script src="https://unpkg.com/react-grab/dist/index.global.js"></script> </head> <body> <div id="root"></div> <script src="./renderer.js"></script> </body> </html>
Step F2 -
:main.js
const { app, BrowserWindow } = require("electron") const path = require("path") function createWindow() { const mainWindow = new BrowserWindow({ width: 1200, height: 800, webPreferences: { preload: path.join(__dirname, "preload.js"), contextIsolation: true, nodeIntegration: false, sandbox: false // Required for react-grab source mapping } }) mainWindow.loadFile("index.html") if (process.env.NODE_ENV === "development") { mainWindow.webContents.openDevTools() } } app.whenReady().then(createWindow)
Step F3 -
:renderer.js
// Initialize react-grab (loaded globally via script tag) if (window.ReactGrab) { window.ReactGrab.init() }
Step 4: Verify Installation
Run the dev server and check:
- Open browser DevTools console
- Look for:
message[react-grab] initialized - Hover over any element and press Cmd+C (Mac) or Ctrl+C (Windows)
- Paste - you should see component info
Expected output:
<button class="btn">Submit</button> in SubmitButton components/button.tsx:42:19
Troubleshooting
All Projects
| Issue | Solution |
|---|---|
| "react-grab not defined" | Ensure script loads before React renders |
| Nothing copied on Cmd/Ctrl+C | Check browser console for errors |
| Wrong file paths | Enable source maps in bundler config |
Electron-Specific
| Issue | Solution |
|---|---|
| CSP blocking script | Add to CSP |
| Source maps not working | Set in webPreferences |
| Keyboard shortcut not working | Check for conflicting Menu accelerators |
| DevTools eval warning | Expected in dev - CSP needs |
CSP Reference for Electron
Via Session Headers (Recommended):
// In main process after creating BrowserWindow if (process.env.NODE_ENV === "development") { mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => { callback({ responseHeaders: { ...details.responseHeaders, "Content-Security-Policy": [ "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'" ] } }) }) }
Via Meta Tag:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'" />
Optional: Claude Code Integration
Add to
scripts:package.json
| Project Type | Script |
|---|---|
| Next.js | |
| Vite | |
| electron-vite | |
| Electron Forge | |
Client-side (add after react-grab init):
if (import.meta.env.DEV) { import("react-grab").then(() => { const script = document.createElement("script") script.src = "//unpkg.com/@react-grab/claude-code/dist/client.global.js" document.head.appendChild(script) }) }