はじめに
AIコーディングエージェントに「ヘッダー右上のログインボタンのデザインを直して」と頼んだのに、まったく違うコンポーネントを編集され始めた——そんな経験はないでしょうか。言葉でUIの場所を説明するのは想像以上に難しく、エージェントは似た名前のコンポーネントやスタイルが近い別要素を誤って選んでしまいがちです。
React-Grabは、この「言葉での指示」を「実物の指差し」に変えてくれる開発ツールです。ブラウザ上でマウスを乗せた要素をそのままコピーするだけで、Reactのコンポーネントスタックとソースの場所(ファイル名・行番号)までまとめてエージェントに渡せます。
とはいえ、読むより触った方が早いと思います。実際にブラウザ上で要素を選択して情報を取り出せる場所を用意したので、先に挙動を見たい方はこちらからどうぞ。
React-Grabとは
React-Grabは、開発中のブラウザ画面からUI要素を選んで、その要素の情報をクリップボードにコピーする開発ツールです。コピーされる内容には対象要素とそれを描画しているReactコンポーネントのスタック、ソースの位置情報が含まれており、AIコーディングエージェントに「この要素です」と正確に伝えられます。GitHub上ではAiden Bai氏(React Scanの開発者としても知られています)によって開発されており、活発にメンテナンスが続いています。
主な特徴
- ホバー&ショートカットで完結 - 要素にマウスを乗せて
⌘C/Ctrl+Cを押すだけでコピーが完了します - コンポーネントスタックとソース位置を同梱 -
LoginForm (at components/login-form.tsx:46:19)のような形式で、対象要素がどのコンポーネントのどの行から描画されているかまで分かります - 開発モード限定で安全 -
NODE_ENV === "development"のときだけ有効になり、本番ビルドには含まれません react-grab/primitivesで拡張可能 - 要素のヒットテストや情報取得のAPIが公開されており、自分専用のピッカーUIを組み立てられます
インストール
プロジェクトのルートで次のコマンドを実行すると、使用しているフレームワークを自動判定してセットアップしてくれます。
npx grab@latest init
CLIが使えない場合は、フレームワークごとに手動でスクリプトを読み込むこともできます。Viteの場合はエントリーファイルの先頭に以下を追加します。
if (import.meta.env.DEV) {
import("react-grab");
}
Webpackの場合は、まずパッケージをインストールしてから同様に読み込みます。
npm install react-grab
React-Grabのサンプルを動かす
React-Grabの核となる操作は、要素にマウスを乗せてCtrl+C(Macは⌘C)を押すことです。コピーが成功するとreact-grab:element-selectedというCustomEventがwindowに発火し、選択された要素のタグ名・id・クラス名・テキスト内容(Reactアプリ内であればコンポーネント名やソース位置も)がevent.detail.elementsに入ってきます。
import "react-grab@0.2.0";
window.addEventListener("react-grab:element-selected", (event) => {
const [element] = event.detail.elements;
console.log(element.tagName, element.componentName, element.filePath);
});
下のサンプルでは、カード内の見出しやボタンにマウスを乗せてCtrl+Cを押すと、選択された要素の情報が画面下のログにそのまま表示されます。何も選択されていない状態と、選択した直後で表示内容がどう変わるか比べてみてください(このサンプルはプレーンなHTMLなのでcomponentNameは空欄になりますが、実際のReactアプリで使うとそこにコンポーネント名が入ります)。
ボタンと見出しでコピーしてみると、tagNameやtextContentが要素ごとに変わることが分かります。実際のプロジェクトでは、この内容をそのままAIエージェントのチャットに貼り付けるだけで、「どの要素の話をしているか」を正確に伝えられます。
基本的な使い方
React-Grabはimport "react-grab"を実行するだけで初期化され、以降は画面のどこでもホバー&ショートカットが有効になります。Next.js(App Router)の場合は、app/layout.tsxで開発モード限定のスクリプトタグとして読み込みます。
import Script from "next/script";
export default function RootLayout({ children }) {
return (
<html>
<head>
{process.env.NODE_ENV === "development" && (
<Script
src="//unpkg.com/react-grab/dist/index.global.js"
crossOrigin="anonymous"
strategy="beforeInteractive"
/>
)}
</head>
<body>{children}</body>
</html>
);
}
Pages Routerの場合はpages/_document.tsxに同様のスクリプトを追加します。フレームワークを問わず共通しているのは、開発モードのときだけ読み込むという点です。React-Grabは本番ビルドに混入しないよう設計されているので、この条件分岐は省略せずに書いておきましょう。
実践的なユースケース
React-Grabはショートカット操作だけでなく、react-grab/primitivesというモジュールで内部のAPIを公開しています。ここでは代表的な3つの使い方を紹介します。
primitivesでカスタムピッカーを作る
デフォルトの挙動をそのまま使うのではなく、「特定のコンテナの中だけ選択可能にしたい」「ツールバー自体は選択対象から外したい」といったカスタマイズをしたい場合は、getElementAtPointとisElementGrabbableを組み合わせて自前のヒットテストを実装できます。data-react-grab-ignore属性を付けた要素は、標準の判定でも自動的に除外対象になります。
import { getElementAtPoint, isElementGrabbable } from "react-grab/primitives";
const target = getElementAtPoint(event.clientX, event.clientY, {
container: appElement,
filter: (candidate) => isElementGrabbable(candidate) && !toolbar.contains(candidate),
});
下のサンプルでは、マウスを動かすたびにgetElementAtPointでカーソル直下の要素を取得し、isElementGrabbableで選択可能かどうかを判定しています。data-react-grab-ignoreを付けた「ignore」ボックスの上だけは「対象なし」のままになる点に注目してください。
filterに渡す関数を差し替えるだけで、独自ルールのピッカーUIをそのまま組み立てられるのがポイントです。
getElementContextで要素情報をプログラムから取得する
ショートカットキーを介さず、任意のタイミングで要素の情報を取得したいこともあります。そんなときはgetElementContextを使うと、React-Grabがクリップボードにコピーするのと同じ内容(snippet)に加えて、コンポーネント名やソース位置を構造化されたオブジェクトとして受け取れます。
import { getElementContext } from "react-grab/primitives";
const ctx = await getElementContext(document.querySelector(".my-button"));
ctx.snippet; // クリップボードにコピーされるのと同じ整形済みテキスト
ctx.componentName; // "SubmitButton"
ctx.filePath; // "/src/components/Button.tsx"
下のサンプルでは、ボタンを押した瞬間に対象要素のgetElementContextを呼び出し、返ってきたsnippetをそのまま画面に表示しています。ショートカット操作なしで、任意の要素を任意のタイミングで取得できることが確認できます。
アクティベーションキーとオプションをカスタマイズする
デフォルトの⌘C/Ctrl+Cは他のショートカットと衝突することがあります。そんなときはinit()を自分で呼び出し、activationKeyで任意のキー組み合わせに変更できます。自動初期化を止めるために、インポート前にwindow.__REACT_GRAB_DISABLED__を立てておくのがポイントです。
window.__REACT_GRAB_DISABLED__ = true;
const { init } = await import("react-grab");
init({
activationKey: "ctrl+g",
maxContextLines: 1,
});
下のサンプルでは、ショートカットをCtrl+Gに変更しています。ボタンにマウスを乗せてCtrl+Gを押すと、通常のCtrl+Cのときと同じようにreact-grab:element-selectedイベントが発火します。activationKeyの文字列を"alt+shift+c"のように書き換えると、割り当てるキーの組み合わせも自由に変えられます。
チーム内で既存のショートカットと衝突する場合や、maxContextLinesを増やしてより深いコンポーネント階層まで含めたい場合など、init()のオプションを使えば運用に合わせて細かく調整できます。
まとめ
React-Grabは、AIコーディングエージェントに「どの要素の話をしているか」を正確に伝えるための小さくて実用的なツールです。ホバーしてショートカットを押すだけという操作のシンプルさに対して、コンポーネントスタックとソース位置まで含めてコピーしてくれる情報量は頼もしく感じられます。
react-grab/primitivesまで踏み込めば、ショートカットに頼らない独自のピッカーUIや、任意のタイミングで要素情報を取得する仕組みも組み立てられます。まずはnpx grab@latest initで手元のプロジェクトに導入し、実際にエージェントとのやり取りがどれだけスムーズになるか試してみてください。
