React bindings for @calimero-network/mero-js — the Calimero Network SDK.
No UI components. No styled-components. No axios. Just a provider, hooks, and storage helpers.
pnpm add @calimero-network/mero-react @calimero-network/mero-jsPeer dependencies: react ^18 || ^19, react-dom ^18 || ^19.
import { MeroProvider, useMero, useExecute, useSubscription, AppMode } from '@calimero-network/mero-react';
function App() {
return (
<MeroProvider mode={AppMode.SingleContext} packageName="com.calimero.my-app">
<MyApp />
</MeroProvider>
);
}
function MyApp() {
const { isAuthenticated, connectToNode, logout, contextId, contextIdentity } = useMero();
if (!isAuthenticated) {
return <button onClick={() => connectToNode('http://localhost:4001')}>Connect</button>;
}
return <Dashboard />;
}
function Dashboard() {
const { contextId, contextIdentity } = useMero();
const { execute, loading } = useExecute(contextId, contextIdentity);
const [items, setItems] = useState([]);
// Real-time updates via SSE
useSubscription(
contextId ? [contextId] : [],
() => fetchItems(),
);
const fetchItems = async () => {
const data = await execute('list');
if (data) setItems(data);
};
const addItem = async (title: string) => {
await execute('add', { title });
await fetchItems();
};
return (
<div>
{loading && <p>Loading...</p>}
{items.map(item => <div key={item.id}>{item.title}</div>)}
<button onClick={() => addItem('New item')}>Add</button>
</div>
);
}Wraps your app with a MeroJs instance, auth state, and SSE connectivity.
<MeroProvider
mode={AppMode.SingleContext} // required
packageName="com.calimero.my-app" // for package-based apps
timeoutMs={30000} // optional, default 30s
>
{children}
</MeroProvider>Props (MeroProviderConfig & { children }):
| Prop | Type | Required | Description |
|---|---|---|---|
mode |
AppMode |
Yes | SingleContext, MultiContext, or Admin |
packageName |
string |
No | Package name for registry/node lookup |
packageVersion |
string |
No | Specific version (defaults to latest) |
registryUrl |
string |
No | Registry URL override |
timeoutMs |
number |
No | HTTP request timeout (default 30000) |
Modes and their permissions:
| Mode | Permissions | Use case |
|---|---|---|
SingleContext |
context:execute |
Apps that work with one context |
MultiContext |
context:create, context:list, context:execute |
Apps managing multiple contexts |
Admin |
admin |
Admin dashboards, dev tools |
Auth flow: when connectToNode(url) is called, the provider redirects to the node's auth page. After login, the node redirects back with tokens in the URL hash. The provider processes these once (StrictMode-safe via ref) and sets isAuthenticated = true.
Online detection: the provider opens an SSE connection to the node after auth. isOnline reflects the SSE connection state — no polling.
Access the MeroJs instance, auth state, and actions.
const {
mero, // MeroJs | null — the SDK instance
isAuthenticated, // boolean
isOnline, // boolean — SSE connection state
isLoading, // boolean — initial session restore
nodeUrl, // string | null
applicationId, // string | null — resolved from auth callback
contextId, // string | null — from auth callback
contextIdentity, // string | null — executor public key from auth callback
connectToNode, // (url: string) => void — starts auth redirect
logout, // () => void — clears tokens and state
} = useMero();Through mero you access the full MeroJs API:
// Admin API (flat methods, NOT nested)
await mero.admin.healthCheck();
await mero.admin.getContexts();
await mero.admin.getContext(contextId);
await mero.admin.getContextIdentitiesOwned(contextId);
await mero.admin.listApplications();
await mero.admin.getApplication(appId);
await mero.admin.installApplication(request);
await mero.admin.createContext(request);
await mero.admin.uploadBlob(request);
await mero.admin.getPeersCount();
// Auth API
await mero.auth.getProviders();
await mero.auth.generateTokens(request);
await mero.auth.refreshToken(request);
// RPC
await mero.rpc.execute({ contextId, method, argsJson, executorPublicKey });
// SSE events
mero.events.connect();
mero.events.subscribe(contextIds);
mero.events.on('event', handler);
// Tokens
mero.getTokenData(); // { access_token, refresh_token, expires_at } | null
mero.isAuthenticated(); // booleanWraps mero.rpc.execute() with loading/error state. Unmount-safe.
const { execute, loading, error } = useExecute(contextId, contextIdentity);
// Generic typed
const todos = await execute<Todo[]>('list');
await execute('add', { title: 'Buy milk' });
await execute('toggle', { id: '1' });| Return | Type | Description |
|---|---|---|
execute |
<T>(method, params?) => Promise<T | null> |
Call a contract method |
loading |
boolean |
Request in flight |
error |
Error | null |
Last error |
Manages SSE event subscription lifecycle. StrictMode-safe — connects once per MeroJs instance, cleans up on unmount.
useSubscription(
contextId ? [contextId] : [],
(event) => {
console.log('Context event:', event.contextId, event.data);
refreshData();
},
);| Param | Type | Description |
|---|---|---|
contextIds |
string[] |
Context IDs to subscribe to (empty array = no subscription) |
callback |
(event: SseEventData) => void |
Called on each context event |
The SSE connection is shared — multiple useSubscription hooks reuse the same connection. The first one to mount calls connect(), subsequent ones just add handlers.
Fetches contexts from the node, optionally filtered by application ID.
const { contexts, loading, error, refetch } = useContexts(applicationId);
// contexts: Array<{ contextId: string; applicationId: string }>Persist/read node URL, application ID, context ID, and context identity in localStorage.
import {
getNodeUrl, setNodeUrl, clearNodeUrl,
getApplicationId, setApplicationId, clearApplicationId,
clearAllStorage,
} from '@calimero-network/mero-react';These are used internally by MeroProvider but exported for apps that need direct access.
mero-react re-exports everything from mero-js via export * from '@calimero-network/mero-js'. Any new API added to mero-js is automatically available from mero-react — no manual sync needed.
// All of these work from a single import
import {
MeroProvider, useMero, useExecute, useSubscription, // react
MeroJs, RpcClient, SseClient, WsClient, // core
parseAuthCallback, buildAuthLoginUrl, // auth helpers
LocalStorageTokenStore, MemoryTokenStore, // token stores
} from '@calimero-network/mero-react';AppMode.SingleContext // 'single-context'
AppMode.MultiContext // 'multi-context'
AppMode.Admin // 'admin'
ConnectionType.Custom // 'custom'
ConnectionType.Local // 'local'
ConnectionType.Remote // 'remote'import type {
MeroContextValue, // useMero() return type
MeroProviderConfig, // MeroProvider props (without children)
MeroProviderProps, // MeroProvider props (with children)
CustomConnectionConfig,// { type: ConnectionType.Custom, url: string }
AppContext, // { contextId, executorId, applicationId }
ExecutionResult, // { success, result?, error? }
} from '@calimero-network/mero-react';// Provider & hooks (mero-react)
MeroProvider, useMero, MeroContext
useExecute, useSubscription, useContexts
// Enums (mero-react)
// Types (mero-react)
MeroContextValue, MeroProviderConfig, MeroProviderProps
CustomConnectionConfig, AppContext, ExecutionResult
// Storage (mero-react)
localStorageTokenStorage
getNodeUrl, setNodeUrl, clearNodeUrl
getApplicationId, setApplicationId, clearApplicationId
clearAllStorage
// Everything from @calimero-network/mero-js (auto re-exported)
MeroJs, createMeroJs, MeroJsConfig, TokenData
RpcClient, RpcError, ExecuteParams
SseClient, SseEventData, WsClient, WsEventData
AuthApiClient, AdminApiClient
LocalStorageTokenStore, MemoryTokenStore, TokenStore
parseAuthCallback, buildAuthLoginUrl, AuthCallbackResult, AuthLoginOptions
WebHttpClient, HttpClient, HTTPError
// ...and all other mero-js exports
MIT