Skip to content

React API BridgeScoped imperative APIs for React

Expose component methods anywhere in the tree without prop drilling, while keeping scope, typing, async registration, and multi-instance support.

react-api-bridge

Why It Exists

React already handles state well. The hard part is imperative coordination:

  • Open a modal from somewhere deep in the tree
  • Tell a tree node or wizard step to refresh itself
  • Trigger a capability exposed by a plugin-like subtree
  • Access an API that may not be mounted yet

Another way to frame it:

ScenarioReact's answer
Passing data down, one levelprops
Passing data down, across levelsContext
Exposing actions upward, one leveluseImperativeHandle
Exposing actions upward, across levelsBlank (filled by react-api-bridge)

react-api-bridge treats these as scoped imperative APIs instead of event broadcasts.

Mental Model

Think of it as:

  • ref, but not limited to direct parent-child links
  • Context, but for imperative capabilities instead of plain values
  • a command registry, but scoped to React subtrees

Better Than An Event Bus When

Use react-api-bridge when your real intent is:

  • "Call this component capability"
  • "Find the local provider in this subtree"
  • "Reach a parent scope on purpose"
  • "Wait until the provider is mounted"

Use an event bus when your real intent is:

  • "Notify many listeners"
  • "Broadcast an event payload"
  • "Model pub/sub instead of direct capabilities"

Quick Example

tsx
import { useState } from 'react';
import {
  createBoundary,
  createBridge,
  useAPI,
  useRegister
} from '@ryo-98/react-api-bridge';

interface AppAPIs {
  modal: {
    open: (content: string) => void;
    close: () => void;
  };
}

const bridge = createBridge<AppAPIs>();
const Boundary = createBoundary(bridge);

function ModalHost() {
  const [content, setContent] = useState<string | null>(null);

  useRegister(bridge, 'modal', () => ({
    open: setContent,
    close: () => setContent(null)
  }), []);

  if (!content) return null;
  return <dialog open>{content}</dialog>;
}

function OpenButton() {
  const modalAPI = useAPI(bridge, 'modal');

  return (
    <button onClick={() => modalAPI.current?.open('Hello from anywhere')}>
      Open modal
    </button>
  );
}

export default function App() {
  return (
    <Boundary>
      <ModalHost />
      <OpenButton />
    </Boundary>
  );
}

Continue with the Getting Started guide.

MIT Licensed