npm package: @myop/react-native
Description: React Native component for embedding Myop components via WebView
Latest version: 0.0.48

# @myop/react-native

Official React Native bindings for embedding [Myop](https://myop.dev) components in your React Native applications.

Myop components are framework-agnostic UI components that can be updated in real-time without redeploying your application. Build once, embed anywhere, and iterate instantly. Perfect for teams that need to ship UI changes fast, A/B test components, or empower non-developers to customize the interface. Myop is also the safest way to adopt AI-generated components in your application—whether created by developers or non-developers—with built-in sandboxing and controlled integration.

[![npm version](https://img.shields.io/npm/v/@myop/react-native.svg)](https://www.npmjs.com/package/@myop/react-native)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Discord](https://img.shields.io/badge/Discord-Join%20us-5865F2?logo=discord&logoColor=white)](https://myop.dev/discord)

[Website](https://www.myop.dev/) | [Documentation](https://docs.myop.dev/) | [Dashboard](https://dashboard.myop.dev/) | [Discord](https://myop.dev/discord)

## Installation

```bash
npm install @myop/react-native
```

Install the required peer dependency:

```bash
npm install react-native-webview
```

## Auto-Generated React Native Components

Myop automatically generates a typed React Native component package for every component you create in the [dashboard](https://dashboard.myop.dev/). Install it directly via npm:

```bash
npm install https://cloud.myop.dev/npm/{component-id}/react-native
```

Then import and use it like any React Native component. For example, if you created a table component called "users-table" in the dashboard:

```tsx
import { UsersTable } from "@myop/users-table";

export default function App() {
  const users = [
    { id: 1, name: "Alice", email: "alice@example.com", role: "Admin" },
    { id: 2, name: "Bob", email: "bob@example.com", role: "User" },
    { id: 3, name: "Charlie", email: "charlie@example.com", role: "User" },
  ];

  return (
    <UsersTable
      data={{ rows: users }}
      onRowClicked={(payload) => {
        console.log("Selected user:", payload.rowData);
      }}
      onDeleteClicked={(payload) => {
        deleteUser(payload.userId);
      }}
      onExportRequested={(payload) => {
        exportToFormat(payload.format); // "csv" | "xlsx"
      }}
      style={{ flex: 1 }}
    />
  );
}
```

**Why this is powerful:**

- **Fully typed** — The generated package includes complete TypeScript types for your component's data interface and all CTA event payloads
- **Auto loaded** — Components are fetched and rendered automatically, no manual setup required
- **Not in your code** — The actual component implementation lives on Myop and is loaded at runtime. Update your component in the dashboard and it's instantly live—no rebuild, no redeploy
- **Zero bundle impact** — Auto-generated component packages are just thin typed wrappers. The actual component implementations are loaded at runtime via WebView, meaning your Myop components can be as complex, feature-rich, and heavy as you need without adding to your application bundle

**Environment options:**

Set the default environment for all components using `setEnvironment`:

```tsx
import { setEnvironment } from "@myop/react-native";

// Set default environment for all component loads
setEnvironment("staging");
```

You can also override the environment directly on a specific component:

```tsx
<UsersTable
  data={{ rows: users }}
  preview={true}        // Load unpublished preview version
  environment="staging" // Load from specific environment (prod, staging, etc.)
  style={{ flex: 1 }}
/>
```

Environments are fully configurable in the [dashboard](https://dashboard.myop.dev/), allowing you to test changes in staging before publishing to production.

For more details on auto-generated packages, see the [Auto-Generated Packages documentation](https://docs.myop.dev/docs/learnMyop/AutoGeneratedPackages).

## Requirements

| Dependency | Version |
|------------|---------|
| React | >= 16.8.0 |
| React Native | >= 0.60.0 |
| react-native-webview | >= 11.0.0 |

## Quick Start

```tsx
import { MyopComponent } from '@myop/react-native';

export default function App() {
  return (
    <MyopComponent
      componentId="your-component-id"
      style={{ flex: 1 }}
    />
  );
}
```

## Usage

### Loading by Component ID

The simplest way to embed a Myop component:

```tsx
<MyopComponent
  componentId="abc123"
  style={{ flex: 1 }}
/>
```

### Passing Data to Components

Use the `data` prop to send data to your component's `myop_init_interface`:

```tsx
const [userData, setUserData] = useState({ name: 'John', theme: 'dark' });

<MyopComponent
  componentId="abc123"
  data={userData}
  style={{ flex: 1 }}
/>
```

When `data` changes, `myop_init_interface` is automatically called with the new value.

### Handling Component Events

Listen for events from your component's `myop_cta_handler`:

```tsx
<MyopComponent
  componentId="abc123"
  on={(action, payload) => {
    switch (action) {
      case 'submit':
        handleSubmit(payload);
        break;
      case 'navigate':
        navigation.navigate(payload.screen);
        break;
    }
  }}
  style={{ flex: 1 }}
/>
```

### Component Proxy API

Access the component instance for direct manipulation:

```tsx
import { IMyopComponentProxy } from '@myop/react-native';

const [component, setComponent] = useState<IMyopComponentProxy | null>(null);

<MyopComponent
  componentId="abc123"
  onLoad={(proxy) => setComponent(proxy)}
  style={{ flex: 1 }}
/>

// Send data to component
component?.props.myop_init_interface({ theme: 'dark' });

// DOM manipulation
component?.element.style.set('opacity', '0.5');
component?.element.set('style.background', 'red');

// Get values (async)
const opacity = await component?.element.style.get('opacity');
const scrollTop = await component?.element.get('scrollTop');

// Lifecycle methods
component?.hide();
component?.show();
component?.dispose();
```

### Loading & Error States

By default, no loader is shown while the component loads. The Myop-branded fallback is displayed automatically on error.

### Using the Default Myop Loader

```tsx
import { MyopComponent, MyopLoader } from '@myop/react-native';

<MyopComponent
  componentId="abc123"
  loader={<MyopLoader />}  // Opt-in to default Myop loader
  fadeDuration={300}
  style={{ flex: 1 }}
/>
```

### Custom Loading State

Provide your own loading component:

```tsx
import { ActivityIndicator, View } from 'react-native';

<MyopComponent
  componentId="abc123"
  loader={
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <ActivityIndicator size="large" color="#0000ff" />
    </View>
  }
  fadeDuration={300}
  style={{ flex: 1 }}
/>
```

### Custom Error Fallback

Display a custom view when the component fails to load:

```tsx
import { MyopComponent, MyopFallback } from '@myop/react-native';

<MyopComponent
  componentId="abc123"
  fallback={
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Unable to load component</Text>
    </View>
  }
  onError={(error) => console.error('Load failed:', error)}
  style={{ flex: 1 }}
/>

// Or use the default Myop fallback explicitly
<MyopComponent
  componentId="abc123"
  fallback={<MyopFallback />}
  style={{ flex: 1 }}
/>
```

### Using Component Configuration

For advanced use cases, pass a full configuration object:

```tsx
import { MyopComponent, IComponentInstanceConfig } from '@myop/react-native';

const config: IComponentInstanceConfig = {
  id: 'instance-1',
  componentId: 'abc123',
  componentName: 'MyComponent',
  skinSelector: {
    type: 'Dedicated',
    skin: { id: 'skin-1' }
  }
};

<MyopComponent
  componentConfig={config}
  style={{ flex: 1 }}
/>
```

### Environment Selection

Target different deployment environments:

```tsx
<MyopComponent
  componentId="abc123"
  environment="staging"  // defaults to "production"
  style={{ flex: 1 }}
/>
```

### WebView Behavior

Control scroll, zoom, and text selection:

```tsx
<MyopComponent
  componentId="abc123"
  scrollEnabled={true}      // Enable scrolling (default: false)
  zoomEnabled={true}        // Enable pinch-to-zoom (default: false)
  selectionEnabled={true}   // Enable text selection (default: false)
  style={{ flex: 1 }}
/>
```

### Preloading Components

Preload components for instant rendering when they're displayed:

```tsx
import { preloadComponents, isPreloaded } from '@myop/react-native';

// Preload on app startup or before navigating
await preloadComponents(['component-1', 'component-2']);

// Check if component is preloaded
if (isPreloaded('component-1')) {
  // Component will render instantly without loader
}
```

### Configuration APIs

Configure the CloudRepository for custom endpoints or local development:

```tsx
import {
  enableLocalDev,
  setCloudRepositoryUrl,
  setCloudRepository,
  setEnvironment,
  getCloudRepository
} from '@myop/react-native';

// Enable local development mode (connects to localhost:9292)
enableLocalDev();

// Use a custom endpoint
setCloudRepositoryUrl('https://custom.myop.dev');

// Set default environment for all components
setEnvironment('staging');

// Get the current CloudRepository instance
const repo = getCloudRepository();
```

## API Reference

### MyopComponent Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `componentId` | `string` | - | Component ID to load from Myop cloud |
| `componentConfig` | `IComponentInstanceConfig` | - | Full component configuration object |
| `data` | `any` | - | Data passed to `myop_init_interface` |
| `on` | `(action: string, payload?: any) => void` | - | Handler for `myop_cta_handler` events |
| `onLoad` | `(proxy: IMyopComponentProxy) => void` | - | Callback with component proxy when loaded |
| `onError` | `(error: string) => void` | - | Callback when component fails to load |
| `loader` | `ReactNode` | `null` | Loading component (default: no loader). Use `<MyopLoader />` for default Myop loader |
| `fallback` | `ReactNode` | `<MyopFallback />` | Error fallback component (default: Myop-branded fallback) |
| `fadeDuration` | `number` | `200` | Loader fade-out duration in ms |
| `environment` | `string` | `"production"` | Target environment |
| `preview` | `boolean` | `false` | Load preview version of component |
| `scrollEnabled` | `boolean` | `false` | Enable WebView scrolling |
| `zoomEnabled` | `boolean` | `false` | Enable pinch-to-zoom |
| `selectionEnabled` | `boolean` | `false` | Enable text selection |
| `style` | `StyleProp<ViewStyle>` | - | Container styles |

Either `componentId` or `componentConfig` must be provided.

### Exported Components

| Component | Description |
|-----------|-------------|
| `MyopComponent` | Main component for embedding Myop content |
| `MyopLoader` | Default animated loading state |
| `MyopFallback` | Default error/fallback state |

### Exported Functions

| Function | Description |
|----------|-------------|
| `preloadComponents(ids, env?, preview?)` | Preload components for instant rendering |
| `isPreloaded(componentId, env?, preview?)` | Check if a component is cached |
| `enableLocalDev()` | Enable local development mode (localhost:9292) |
| `setCloudRepositoryUrl(url)` | Set a custom CloudRepository URL |
| `setCloudRepository(repository)` | Set a custom CloudRepository instance |
| `setEnvironment(env)` | Set the default environment |
| `getCloudRepository()` | Get the current CloudRepository instance |

### Types

```typescript
interface IMyopComponentProxy {
  id: string;
  props: {
    myop_init_interface: (data: any) => void;
    myop_cta_handler: ((action: string, payload?: any) => void) | null;
  };
  element: IElementProxy;
  dispose: () => void;
  hide: () => void;
  show: () => void;
  inspect: () => void;
}

interface IElementProxy {
  set: (path: string, value: any) => void;
  get: (path: string) => Promise<any>;
  style: {
    set: (property: string, value: string) => void;
    get: (property: string) => Promise<string>;
  };
}

interface IComponentInstanceConfig {
  id: string;
  componentId: string;
  componentName: string;
  skinSelector: ISkinSelectorConfig;
  nestedComponents?: IComponentInstanceConfig[];
  resolvedExperiences?: IExperience[];
  resolvedNestedComponents?: IComponentConfig[];
  [key: string]: any;
}

interface MyopLoaderRef {
  fadeOut: (duration?: number) => void;
}
```

## License

MIT