React and Next.js
@hydraone-sdk/core/react provides a provider and three hooks on top of the core client. It requires react 18 or later as a peer dependency.
Provider
Wrap your app once. The provider creates a WalletBridgeClient, a GameAuthManager and a storage adapter, and keeps them stable across renders.
import React from 'react';
import { HydraOneProvider, useWallet } from '@hydraone-sdk/core/react';
function Game() {
const { isConnected, address, balanceADA, connect, disconnect } = useWallet();
// TEMP auth hidden: const { isAuthenticated, signIn, signOut, error } = useHydraAuth();
return (
<div>
<p>{isConnected ? `${address} (${balanceADA} ADA)` : 'Not connected'}</p>
<button onClick={() => (isConnected ? disconnect() : connect())}>
{isConnected ? 'Disconnect' : 'Connect'}
</button>
{/* TEMP auth hidden
<button
onClick={() => (isAuthenticated ? signOut() : signIn({ challenge: 'server-issued-nonce' }))}
>
{isAuthenticated ? 'Sign out' : 'Sign in'}
</button>
{error && <p role="alert">{error.message}</p>}
*/}
</div>
);
}
export function App() {
return (
<HydraOneProvider appCenterOrigin="https://alpha.hydraone.app" autoConnect>
<Game />
</HydraOneProvider>
);
}
Provider props
| Prop | Type | Default | Description |
|---|---|---|---|
client | WalletBridgeClient | created | Provide your own client. |
storage | IStorage | created | Defaults to SafeLocalStorageAdapter in a browser, InMemoryStorageAdapter on the server. |
appCenterOrigin | string | string[] | none | Creates a PostMessageTransport for the default client. The provider destroys it on unmount. |
options | Partial<WalletBridgeClientOptions> | none | Extra client options. |
autoConnect | boolean | false | Run client.init() after mount, in the browser only. |
autoRefreshBalance | boolean | true | Default for useWallet. |
Defaults worth knowing:
- With
appCenterOrigin, the provider creates aPostMessageTransportand destroys it on unmount. Pass an array to allow several hosts. Without it (and withoutoptions.transport) the provider throws, because the client has nothing to connect through. Aclientoroptions.transportyou pass in is never destroyed for you. - In Next.js App Router you can import the provider from a Server Component such as
layout.tsx;@hydraone-sdk/core/reactalready starts with"use client". - The default storage is
SafeLocalStorageAdapterin the browser andInMemoryStorageAdapteron the server. autoConnectcallsclient.init()in an effect, so it never runs during server rendering.
Use the host storage relay
To keep the session alive on Safari, pass a HostStorageRelayAdapter that shares the transport with the client:
import React from 'react';
import { PostMessageTransport, HostStorageRelayAdapter } from '@hydraone-sdk/core';
import { HydraOneProvider, useWallet } from '@hydraone-sdk/core/react';
const transport = new PostMessageTransport({ appCenterOrigin: 'https://alpha.hydraone.app' });
const storage = new HostStorageRelayAdapter({ transport });
function Balance() {
const { balanceADA, isConnected } = useWallet();
return <p>{isConnected ? `${balanceADA} ADA` : 'Not connected'}</p>;
}
export function App() {
return (
<HydraOneProvider options={{ transport }} storage={storage} autoConnect>
<Balance />
</HydraOneProvider>
);
}
Hooks
useWallet(options?)
Returns reactive wallet state and actions.
State: client, connectionState, isConnected, address, usedAddresses, balanceADA (string or null), balanceLovelace (bigint or null), networkId, hostInfo, isAudioMuted, theme.
Actions: connect, disconnect, refreshBalance, signTx, submitTx, signData, setOrientation, triggerHaptic, requestDepositModal, getPlayerProfile.
Options: client, autoConnect, autoRefreshBalance (default true).
useHostStorage(options?)
Returns storage, isAvailable, getItem, setItem, removeItem, clear. It uses the provider's storage unless you pass storage.
useHydraOneContext()
Returns { client, authManager, storage, autoRefreshBalance }. It throws a clear error when used outside <HydraOneProvider>.
Next.js (App Center router)
The hooks use browser APIs, so use them in client components. Create the provider in a client component and render it from your layout:
'use client';
import React, { useMemo } from 'react';
import { PostMessageTransport, HostStorageRelayAdapter } from '@hydraone-sdk/core';
import { HydraOneProvider } from '@hydraone-sdk/core/react';
export function Providers({ children }: { children: React.ReactNode }) {
const { transport, storage } = useMemo(() => {
const t = new PostMessageTransport({ appCenterOrigin: 'https://alpha.hydraone.app' });
return { transport: t, storage: new HostStorageRelayAdapter({ transport: t }) };
}, []);
return (
<HydraOneProvider options={{ transport }} storage={storage}>
{children}
</HydraOneProvider>
);
}
Server rendering is safe: nothing touches window until an effect runs. Balances and addresses are empty on the server and fill in after the client connects, so render a neutral state first to avoid hydration mismatches.
Testing
Mock the client in your component tests: provide a stub ITransport through options.transport and render with @testing-library/react. See Testing and health checks.
