---
title: React Host
description: "Embed Myop components in a React app with @myop/react. The modern MyopComponent API — installation, data binding, typed CTAs, preloading, local dev, auto-generated typed packages."
keywords: [myop react, myop host react, MyopComponent, myop embed react, myop react integration]
sidebar_position: 3.5
---
# React Host

This page covers **embedding** Myop components in a host React app using
`@myop/react`. If you want to **author** a Myop component, see the
[CLI guide](/docs/cli) instead.

> Looking for the API reference? [`@myop/react` reference](/docs/sdk-react) ·
> [Single-file SDK reference](pathname:///sdk-reference.md)

## Installation

```bash
npm install @myop/react @myop/sdk
```

`@myop/sdk` is a peer dependency. Requires **React 18+**.

## Minimal example

```tsx
import { MyopComponent } from "@myop/react";

export function App() {
  return (
    <MyopComponent
      componentId="your-component-id"
      data={{ title: "Hello", items: ["a", "b"] }}
      onItemSelected={(payload) => console.log(payload)}
      style={{ width: 400, height: 300 }}
    />
  );
}
```

That's the whole contract. `componentId` is a UUID from the
[Myop dashboard](https://dashboard.myop.dev). Everything else is optional.

CTA actions emitted by the component show up as `on<PascalCase>` props on the
host — see [Receiving events](#receiving-events-cta-handlers) below.

:::danger Never create iframes manually
Always use `<MyopComponent>`. Never hand-roll an `<iframe>` pointing at a
Myop component — the SDK handles iframe lifecycle, messaging, sizing,
caching, and error recovery for you.
:::

## Passing data in

The `data` prop is delivered to the component's `myop_init_interface`. It's
reactive — updates trigger a re-init inside the iframe:

```tsx
const [count, setCount] = useState(0);

<MyopComponent
  componentId="counter"
  data={{ count }}
/>
```

`data` can be any JSON-serializable value. Functions are not transferred.

## Receiving events (CTA handlers)

Components emit CTA events via `myop_cta_handler(action, payload)`. The host
receives them through **typed per-action props** — `kebab-case` actions become
`onPascalCase` props. **This is the canonical form; use it in new code.**

```tsx
<MyopComponent
  componentId="task-list"
  data={{ tasks }}
  onTaskToggled={({ taskId, completed }) => {
    setTasks((prev) =>
      prev.map((t) => (t.id === taskId ? { ...t, completed } : t))
    );
  }}
  onTaskDeleted={({ taskId }) => {
    setTasks((prev) => prev.filter((t) => t.id !== taskId));
  }}
/>
```

| Component CTA action | React prop |
|----------------------|------------|
| `item-selected` | `onItemSelected` |
| `form-submitted` | `onFormSubmitted` |
| `task-toggled` | `onTaskToggled` |

### Fallback: generic handler

For dynamically named actions or quick experiments, a generic `on` prop
receives every event. Use this only when the typed form doesn't fit:

```tsx
<MyopComponent
  componentId="task-list"
  data={{ tasks }}
  on={(action, payload) => {
    if (action === "task-toggled") setTasks(/* ... */);
  }}
/>
```

Don't mix `on` with `on<Action>` props for the same action — pick one per
component instance.

## Props

| Prop | Type | Description |
|------|------|-------------|
| `componentId` | `string` | Myop component UUID |
| `data` | `TData` | Passed to `myop_init_interface`; reactive |
| `on` | `(action, payload) => void` | Generic CTA handler |
| `on[ActionName]` | `(payload) => void` | Typed handler for one CTA |
| `onLoad` | `(component) => void` | Fired after load completes |
| `onError` | `(error: string) => void` | Fired on load failure |
| `style` | `CSSProperties` | Container styles |
| `loader` | `ReactNode` | Custom loading indicator |
| `fallback` | `ReactNode` | Custom error fallback |
| `autoSize` | `boolean` | Auto-size container to content |
| `environment` | `string` | Load from a specific environment |
| `preview` | `boolean` | Load unpublished preview version |

## Auto-generated typed packages

Every Myop component has an auto-generated npm package that bakes in the
`componentId` and exposes a fully-typed component:

```bash
npm install https://cloud.myop.dev/npm/{componentId}/react
```

```tsx
import { TaskList } from "@myop/task-list";

<TaskList
  data={{ tasks }}
  onTaskToggled={(payload) => {
    // payload is typed: { taskId: string; completed: boolean }
  }}
/>
```

Recommended for production. Types come from the component's
`<script type="myop/types">` block.

## Preloading

Eagerly fetch components to avoid loading delay on first render:

```tsx
import { preloadComponents, isPreloaded } from "@myop/react";

await preloadComponents(["id-1", "id-2"]);
```

## Local dev

Point the SDK at a local component dev server (port `9292` by default):

```tsx
import { enableLocalDev } from "@myop/react";

if (import.meta.env.DEV) enableLocalDev();
```

You can also enable via a URL param without code changes — open the app with
`?env=dev`.

Run a component locally with:

```bash
cd path/to/your-component
npx myop dev
```

The dev server registers the component with the local repository and
hot-reloads on file changes.

## Configuration

All SDKs export the same configuration functions:

```ts
import {
  enableLocalDev,
  setCloudRepositoryUrl,
  setEnvironment,
} from "@myop/react";

enableLocalDev();                          // Load from localhost:9292
setCloudRepositoryUrl("https://custom");   // Custom cloud URL
setEnvironment("staging");                 // Default environment
```

## Where to next

- [Auto-generated packages](/docs/learnMyop/AutoGeneratedPackages) — typed components, baked-in `componentId`
- [System architecture](/docs/learnMyop/systemArchitecture) — how the host, cloud, and component runtime fit together
- [`@myop/react` reference](/docs/sdk-react) — full type signatures
- [Host integration (all frameworks)](/docs/cli/host-integration) — same flow for Vue, Angular, React Native
