Skip to main content

Overview

The ReactGrabAPI interface provides programmatic control over React Grab. It’s returned by the init() function and includes methods to activate/deactivate React Grab, manage state, register plugins, and more.

Type Definition

interface ReactGrabAPI {
  activate: () => void;
  deactivate: () => void;
  toggle: () => void;
  comment: () => void;
  isActive: () => boolean;
  isEnabled: () => boolean;
  setEnabled: (enabled: boolean) => void;
  getToolbarState: () => ToolbarState | null;
  setToolbarState: (state: Partial<ToolbarState>) => void;
  onToolbarStateChange: (callback: (state: ToolbarState) => void) => () => void;
  dispose: () => void;
  copyElement: (elements: Element | Element[]) => Promise<boolean>;
  getSource: (element: Element) => Promise<SourceInfo | null>;
  getStackContext: (element: Element) => Promise<string>;
  getState: () => ReactGrabState;
  setOptions: (options: SettableOptions) => void;
  registerPlugin: (plugin: Plugin) => void;
  unregisterPlugin: (name: string) => void;
  getPlugins: () => string[];
  getDisplayName: (element: Element) => string | null;
}

Methods

activate()

Activates React Grab, allowing element selection.
api.activate(): void
Example:
const api = init();
api.activate();
Notes:
  • Only activates if React Grab is currently enabled
  • Clears any pending comment mode
  • Does nothing if already active

deactivate()

Deactivates React Grab, stopping element selection.
api.deactivate(): void
Example:
const api = init();
api.deactivate();
Notes:
  • Works even if currently in a copying state
  • Cleans up all active selections and overlays
  • Restores previously focused elements

toggle()

Toggles React Grab between active and inactive states.
api.toggle(): void
Example:
const api = init();
api.toggle(); // Activates
api.toggle(); // Deactivates

comment()

Activates comment mode, which allows adding a text prompt to copied elements.
api.comment(): void
Example:
const api = init();
api.comment();
// Now when you select an element, you'll be prompted for a comment
Notes:
  • If already in comment mode and active, deactivates React Grab
  • Sets pending comment mode flag if not already active

isActive()

Checks if React Grab is currently active.
api.isActive(): boolean
Example:
const api = init();
if (api.isActive()) {
  console.log('React Grab is active');
}
Returns:
  • true if React Grab is active and element selection is enabled
  • false otherwise

isEnabled()

Checks if React Grab is enabled.
api.isEnabled(): boolean
Example:
const api = init();
if (api.isEnabled()) {
  console.log('React Grab is enabled');
}
Returns:
  • true if React Grab is enabled
  • false if disabled
Notes:
  • Enabled state is separate from active state
  • When disabled, React Grab cannot be activated

setEnabled()

Enables or disables React Grab.
api.setEnabled(enabled: boolean): void
enabled
boolean
required
Whether to enable or disable React Grab
Example:
const api = init();
api.setEnabled(false); // Disable React Grab
api.setEnabled(true);  // Enable React Grab
Notes:
  • Disabling React Grab also deactivates it if currently active
  • The enabled state persists in toolbar state

getToolbarState()

Gets the current toolbar state.
api.getToolbarState(): ToolbarState | null
Returns:
interface ToolbarState {
  edge: 'top' | 'bottom' | 'left' | 'right';
  ratio: number; // 0-1, position along the edge
  collapsed: boolean;
  enabled: boolean;
}
Example:
const api = init();
const state = api.getToolbarState();
console.log(`Toolbar is on ${state?.edge} edge`);

setToolbarState()

Updates the toolbar state.
api.setToolbarState(state: Partial<ToolbarState>): void
state
Partial<ToolbarState>
required
Partial toolbar state to update
Example:
const api = init();
api.setToolbarState({
  edge: 'top',
  collapsed: true
});
Notes:
  • Only provided properties are updated
  • Changes are saved to localStorage
  • Triggers toolbar state change callbacks

onToolbarStateChange()

Subscribes to toolbar state changes.
api.onToolbarStateChange(callback: (state: ToolbarState) => void): () => void
callback
(state: ToolbarState) => void
required
Function to call when toolbar state changes
Returns:
  • Unsubscribe function
Example:
const api = init();
const unsubscribe = api.onToolbarStateChange((state) => {
  console.log('Toolbar state changed:', state);
});

// Later: unsubscribe()

dispose()

Cleans up all React Grab resources, event listeners, and state.
api.dispose(): void
Example:
const api = init();
// ... use React Grab ...
api.dispose(); // Clean up when done
Notes:
  • Removes all event listeners
  • Clears all timers and intervals
  • Removes overlay elements from DOM
  • After calling dispose(), a new init() call will work

copyElement()

Programmatically copies one or more elements.
api.copyElement(elements: Element | Element[]): Promise<boolean>
elements
Element | Element[]
required
Element or array of elements to copy
Returns:
  • Promise<boolean> - Resolves to true if copy succeeded, false otherwise
Example:
const api = init();
const button = document.querySelector('button');
if (button) {
  const success = await api.copyElement(button);
  console.log(success ? 'Copied!' : 'Failed');
}
Example - Multiple Elements:
const api = init();
const items = Array.from(document.querySelectorAll('.item'));
const success = await api.copyElement(items);

getSource()

Gets source information for an element.
api.getSource(element: Element): Promise<SourceInfo | null>
element
Element
required
The element to get source info for
Returns:
interface SourceInfo {
  filePath: string;
  lineNumber: number | null;
  componentName: string | null;
}
Example:
const api = init();
const button = document.querySelector('button');
if (button) {
  const source = await api.getSource(button);
  if (source) {
    console.log(`Component: ${source.componentName}`);
    console.log(`File: ${source.filePath}:${source.lineNumber}`);
  }
}
Notes:
  • Returns null if source information cannot be determined
  • Requires React DevTools fiber information to be available

getStackContext()

Gets the stack context for an element.
api.getStackContext(element: Element): Promise<string>
element
Element
required
The element to get stack context for
Returns:
  • Promise<string> - A formatted string with the element’s component stack
Example:
const api = init();
const div = document.querySelector('.container');
if (div) {
  const context = await api.getStackContext(div);
  console.log(context);
  // Output: "App > Layout > Container > div"
}

getState()

Gets the current React Grab state.
api.getState(): ReactGrabState
Returns:
interface ReactGrabState {
  isActive: boolean;
  isDragging: boolean;
  isCopying: boolean;
  isPromptMode: boolean;
  isCrosshairVisible: boolean;
  isSelectionBoxVisible: boolean;
  isDragBoxVisible: boolean;
  targetElement: Element | null;
  dragBounds: DragRect | null;
  grabbedBoxes: Array<{
    id: string;
    bounds: OverlayBounds;
    createdAt: number;
  }>;
  labelInstances: Array<{
    id: string;
    status: SelectionLabelStatus;
    tagName: string;
    componentName?: string;
    createdAt: number;
  }>;
  selectionFilePath: string | null;
  toolbarState: ToolbarState | null;
}
Example:
const api = init();
const state = api.getState();
if (state.isActive && state.targetElement) {
  console.log('Currently hovering:', state.targetElement);
}

setOptions()

Updates React Grab options after initialization.
api.setOptions(options: SettableOptions): void
options
SettableOptions
required
Options to update. Same as Options but without the enabled property.
Example:
const api = init();
api.setOptions({
  activationMode: 'hold',
  keyHoldDuration: 300,
  maxContextLines: 5
});
Notes:
  • Cannot change the enabled option - use setEnabled() instead
  • Changes take effect immediately
  • Affects all registered plugins

registerPlugin()

Registers a new plugin with React Grab.
api.registerPlugin(plugin: Plugin): void
plugin
Plugin
required
The plugin to register
Example:
import { init } from 'react-grab';

const api = init();

api.registerPlugin({
  name: 'my-plugin',
  setup: (api, hooks) => {
    return {
      actions: [{
        id: 'custom-action',
        label: 'Custom Action',
        target: 'context-menu',
        onAction: (context) => {
          console.log('Custom action!', context.element);
        }
      }]
    };
  }
});
Notes:
  • Plugins can add custom actions, hooks, and theme overrides
  • See Plugin API for details

unregisterPlugin()

Unregisters a plugin by name.
api.unregisterPlugin(name: string): void
name
string
required
The name of the plugin to unregister
Example:
const api = init();
api.unregisterPlugin('my-plugin');
Notes:
  • Built-in plugins cannot be unregistered
  • Plugin cleanup methods are called automatically

getPlugins()

Gets a list of registered plugin names.
api.getPlugins(): string[]
Returns:
  • string[] - Array of plugin names
Example:
const api = init();
const plugins = api.getPlugins();
console.log('Registered plugins:', plugins);
// Output: ['copy', 'comment', 'copyHtml', 'copyStyles', 'open', 'my-plugin']

getDisplayName()

Gets the display name (component name or tag name) for an element.
api.getDisplayName(element: Element): string | null
element
Element
required
The element to get the display name for
Returns:
  • Component name if available (e.g., 'Button', 'MyComponent')
  • Tag name if no component name found (e.g., 'div', 'button')
  • null if neither can be determined
Example:
const api = init();
const button = document.querySelector('button');
if (button) {
  const name = api.getDisplayName(button);
  console.log('Display name:', name);
  // Output: "Button" or "button"
}

Usage Example

Here’s a comprehensive example using multiple API methods:
import { init } from 'react-grab';

// Initialize with custom options
const api = init({
  activationMode: 'toggle',
  maxContextLines: 5
});

// Subscribe to state changes
const unsubscribe = api.onToolbarStateChange((state) => {
  console.log('Toolbar moved to:', state.edge);
});

// Programmatically copy an element
const copyButton = async () => {
  const button = document.querySelector('.my-button');
  if (button) {
    const success = await api.copyElement(button);
    if (success) {
      const source = await api.getSource(button);
      console.log('Copied from:', source?.filePath);
    }
  }
};

// Check state
const checkState = () => {
  const state = api.getState();
  console.log('Active:', state.isActive);
  console.log('Target:', api.getDisplayName(state.targetElement));
};

// Cleanup when done
window.addEventListener('beforeunload', () => {
  unsubscribe();
  api.dispose();
});

Build docs developers (and LLMs) love