@myo-devs/react
v0.1.1
Published
Official React SDK for the Myo Platform
Maintainers
Readme
Myo React SDK
Official React SDK for the Myo Platform - Connect your users' personal data through a seamless widget experience.
Installation
npm install @myo-devs/reactQuick Start
The simplest way to add Myo to your app:
import { MyoConnectButton, useMyoConnection } from '@myo-devs/react';
function App() {
const { connection, setConnection, clearConnection } = useMyoConnection({
storage: 'session',
});
if (connection) {
return (
<div>
<p>Connected as {connection.userId}</p>
<button onClick={clearConnection}>Disconnect</button>
</div>
);
}
return (
<MyoConnectButton
fetchToken={async () => {
const res = await fetch('/api/link-token', { method: 'POST' });
return (await res.json()).linkToken;
}}
onSuccess={(metadata) => setConnection(metadata.userId, metadata.provider)}
>
Connect Your Data
</MyoConnectButton>
);
}Features
- One-click connection -
MyoConnectButtonhandles token fetching and widget opening - Custom button support -
useMyoConnecthook for using your own button UI - Connection persistence -
useMyoConnectionhook for storing connection state - Low-level control -
useMyoLinkhook for full customization - Modal & redirect modes - Modal overlay (default) or popup window
- Full TypeScript support - Excellent IDE autocompletion
- Secure PostMessage - Origin-validated communication with widget
Components & Hooks
MyoConnectButton
The easiest way to add Myo connection to your app. Handles fetching the link token and opening the widget automatically.
import { MyoConnectButton, useMyoConnection } from '@myo-devs/react';
function ConnectPage() {
const { connection, setConnection, clearConnection } = useMyoConnection({
storage: 'session',
});
if (connection) {
return (
<div>
<p>Connected: {connection.userId} via {connection.provider}</p>
<button onClick={clearConnection}>Disconnect</button>
</div>
);
}
return (
<MyoConnectButton
fetchToken={async () => {
const res = await fetch('/api/link-token', { method: 'POST' });
const data = await res.json();
if (!res.ok) throw new Error(data.error);
return data.linkToken;
}}
widgetUrl={process.env.NEXT_PUBLIC_MYO_WIDGET_URL}
onSuccess={(metadata) => {
setConnection(metadata.userId, metadata.provider);
}}
onError={(error) => {
console.error('Connection failed:', error.error);
}}
className="connect-button"
>
Connect Your Data
</MyoConnectButton>
);
}MyoConnectButton Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| fetchToken | () => Promise<string> | Yes | Async function to fetch link token from your backend |
| children | ReactNode | Yes | Button content |
| widgetUrl | string | No | Custom widget URL (default: https://link.myo-app.co) |
| onSuccess | (metadata) => void | No | Called on successful connection |
| onError | (error) => void | No | Called when an error occurs |
| onExit | (error, metadata) => void | No | Called when user exits |
| connectingText | string | No | Text while connecting (default: "Connecting...") |
| className | string | No | CSS classes for the button |
| style | CSSProperties | No | Inline styles for the button |
| disabled | boolean | No | Disable the button |
useMyoConnect
Use this hook when you want to trigger the Myo modal from your own custom button or UI element. It handles token fetching and widget opening, giving you full control over the trigger UI.
import { useMyoConnect, useMyoConnection } from '@myo-devs/react';
function CustomConnectButton() {
const { setConnection } = useMyoConnection({ storage: 'session' });
const { connect, isConnecting, isOpen, error } = useMyoConnect({
fetchToken: async () => {
const res = await fetch('/api/link-token', { method: 'POST' });
const data = await res.json();
if (!res.ok) throw new Error(data.error);
return data.linkToken;
},
widgetUrl: process.env.NEXT_PUBLIC_MYO_WIDGET_URL,
onSuccess: (metadata) => {
setConnection(metadata.userId, metadata.provider);
},
onError: (error) => {
console.error('Connection failed:', error.error);
},
});
return (
<div>
<MyFancyButton onClick={connect} disabled={isConnecting || isOpen}>
{isConnecting ? 'Connecting...' : 'Link Your Data'}
</MyFancyButton>
{error && <p className="error">{error}</p>}
</div>
);
}useMyoConnect Options
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| fetchToken | () => Promise<string> | Yes | Async function to fetch link token from your backend |
| widgetUrl | string | No | Custom widget URL (default: https://link.myo-app.co) |
| displayMode | 'modal' \| 'redirect' | No | Widget display mode (default: 'modal') |
| onSuccess | (metadata) => void | No | Called on successful connection |
| onError | (error) => void | No | Called when an error occurs |
| onExit | (error, metadata) => void | No | Called when user exits |
| modalStyle | CSSProperties | No | Override modal container styles |
| iframeStyle | CSSProperties | No | Override iframe styles |
| backdropStyle | CSSProperties | No | Override backdrop styles |
useMyoConnect Return Value
| Property | Type | Description |
|----------|------|-------------|
| connect | () => Promise<void> | Trigger the connection flow (fetch token + open widget) |
| isConnecting | boolean | Whether a token is being fetched |
| isOpen | boolean | Whether the widget is currently open |
| ready | boolean | Whether the widget is ready (token has been fetched) |
| error | string \| null | Latest error message (if any) |
useMyoConnection
Hook for managing persisted connection state. Handles storing/retrieving connection from browser storage.
import { useMyoConnection } from '@myo-devs/react';
function MyComponent() {
const { connection, isLoaded, setConnection, clearConnection } = useMyoConnection({
storage: 'session', // or 'local'
storageKey: 'myo_connection', // optional custom key
});
if (!isLoaded) return <div>Loading...</div>;
if (connection) {
return (
<div>
<p>User: {connection.userId}</p>
<p>Provider: {connection.provider}</p>
<p>Connected at: {new Date(connection.connectedAt).toLocaleString()}</p>
<button onClick={clearConnection}>Disconnect</button>
</div>
);
}
return <div>Not connected</div>;
}useMyoConnection Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| storage | 'session' \| 'local' | 'session' | Storage type |
| storageKey | string | 'myo_connection' | Custom storage key |
useMyoConnection Return Value
| Property | Type | Description |
|----------|------|-------------|
| connection | MyoConnection \| null | Current connection or null |
| isLoaded | boolean | Whether storage has been checked (SSR-safe) |
| setConnection | (userId, provider) => void | Save a connection |
| clearConnection | () => void | Clear the connection |
useMyoLink (Low-Level)
For advanced use cases where you already have a link token and need direct control over the widget. Most developers should use useMyoConnect instead.
import { useMyoLink } from '@myo-devs/react';
function CustomConnect({ linkToken }) {
const { open, close, ready, isOpen, error } = useMyoLink({
token: linkToken,
onSuccess: (metadata) => {
console.log('User ID:', metadata.userId);
console.log('Provider:', metadata.provider); // 'google' | 'meta' | 'upload'
},
onError: (error) => {
console.error('Error:', error.error);
},
onExit: (error, metadata) => {
if (metadata.cancelled) console.log('User cancelled');
},
displayMode: 'modal', // or 'redirect'
});
return (
<button onClick={open} disabled={!ready || isOpen}>
{isOpen ? 'Connecting...' : 'Connect Your Data'}
</button>
);
}useMyoLink Options
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| token | string | Yes | Link token from your backend |
| onSuccess | (metadata) => void | No | Called on successful connection |
| onError | (error) => void | No | Called when an error occurs |
| onExit | (error, metadata) => void | No | Called when user exits |
| displayMode | 'modal' \| 'redirect' | No | Widget display mode (default: 'modal') |
| widgetUrl | string | No | Custom widget URL |
| modalStyle | CSSProperties | No | Override modal container styles |
| iframeStyle | CSSProperties | No | Override iframe styles |
| backdropStyle | CSSProperties | No | Override backdrop styles |
useMyoLink Return Value
| Property | Type | Description |
|----------|------|-------------|
| open | () => void | Open the link widget |
| close | () => void | Close the link widget programmatically |
| ready | boolean | Whether the widget is ready to open |
| isOpen | boolean | Whether the widget is currently open |
| error | LinkErrorMetadata \| null | Latest error (if any) |
Backend Setup
Link tokens must be created on your backend. Example using Next.js API routes:
// app/api/link-token/route.ts
import { NextResponse } from 'next/server';
import { MyoClient } from '@myo-devs/node';
const client = new MyoClient({ apiKey: process.env.MYO_API_KEY });
export async function POST() {
const token = await client.link.tokens.create({
redirectUri: process.env.NEXT_PUBLIC_APP_URL,
});
return NextResponse.json({
linkToken: token.linkToken,
});
}Callback Metadata Types
LinkSuccessMetadata:
{
userId: string;
provider: 'google' | 'meta' | 'upload';
timestamp: number;
}LinkErrorMetadata:
{
error: string;
provider?: 'google' | 'meta' | 'upload';
timestamp: number;
}LinkExitMetadata:
{
cancelled: boolean;
error?: LinkErrorMetadata;
timestamp: number;
}MyoConnection:
{
userId: string;
provider: 'google' | 'meta' | 'upload';
connectedAt: number;
}Security Note
The onSuccess callback is useful for updating your UI immediately, but you should always rely on backend webhooks to confirm the connection was successful. The frontend callback could potentially be spoofed.
Requirements
- React 18+
- React DOM 18+
License
MIT
