A TypeScript/React SDK for embedding a self-custodial Lightning wallet directly in a web app. Your users send and receive Lightning payments with no node to run, no channels to open, and no inbound liquidity to manage, while the keys stay on their own device. The wallet runs entirely in the browser as WebAssembly: there is no backend to operate and nothing listening on a socket. Your app drives it through a small, typed client.
| Package | What it is |
|---|---|
@lightninglabs/wavelength-core |
The contract: types, the WavelengthClient interface, errors, and enums. No DOM, no transport. |
@lightninglabs/wavelength-web |
The browser (wasm) transport. Framework-agnostic: use it directly from vanilla JS, Vue, Svelte, or React. Re-exports core. |
@lightninglabs/wavelength-react-native |
The React Native transport. Re-exports core. |
@lightninglabs/wavelength-react |
React provider + hooks. |
wavelength-react is transport-agnostic: it depends only on core and takes an
injected engine, so the same binding runs over both the web and React Native
transports. wavelength-web and wavelength-react-native each re-export every
type from core, so an app imports the client and its types from one place.
# React (the binding + the web transport)
npm install @lightninglabs/wavelength-react @lightninglabs/wavelength-web
# Vanilla / Vue / Svelte (transport only)
npm install @lightninglabs/wavelength-webYou build the engine with createWebWalletEngine() from wavelength-web (or
createNativeWalletEngine() from wavelength-react-native on mobile). In React
you pass that engine to WavelengthProvider; the provider itself is
transport-agnostic and works the same way with either.
import {
WavelengthProvider,
useWallet,
useWalletBalance,
useWalletSend,
} from "@lightninglabs/wavelength-react";
import { createWebWalletEngine, defaultConfig } from "@lightninglabs/wavelength-web";
// Build the engine once. runtimeBaseUrl points at the hosted wasm runtime
// assets (see below). config + autoStart boot the embedded wallet as soon as
// the wasm runtime is ready.
const engine = createWebWalletEngine({
runtimeBaseUrl: "https://your-host/wavewalletdk/",
config: defaultConfig("signet"),
autoStart: true,
});
function Root() {
return (
<WavelengthProvider engine={engine}>
<Wallet />
</WavelengthProvider>
);
}
function Wallet() {
const { phase } = useWallet();
const balance = useWalletBalance();
const { send } = useWalletSend();
if (phase !== "ready") return <p>Loading… ({phase})</p>;
return (
<div>
<p>Spendable: {balance?.confirmedSat ?? 0} sats</p>
<button onClick={() => send({ invoice: "lnbc…" })}>Pay</button>
</div>
);
}Focused hooks are available when you only need a slice. State-reading hooks
like useWalletBalance() and useWalletActivity() return their value directly.
Mutation hooks like useWalletSend(), useWalletReceive(), and useWalletDeposit()
each expose an action plus verb-prefixed state, e.g. useWalletSend() returns
{ send, sendPending, sendError, sendData, resetSend }.
import {
createWebClient,
defaultConfig,
WalletState,
} from "@lightninglabs/wavelength-web";
const client = createWebClient({ runtimeBaseUrl: "https://your-host/wavewalletdk/" });
await client.ready(); // wasm runtime loaded
await client.start(defaultConfig("signet")); // boot the embedded wallet
const info = await client.getInfo();
if (info.walletState === WalletState.None) {
const { mnemonic } = await client.createWallet({ password: "…" });
// back up `mnemonic`
}
const balance = await client.balance();
const { invoice } = await client.receive({ amountSat: 1000 });defaultConfig(network) returns a ready-to-use config preloaded with the
canonical public endpoints for signet and testnet. Override only
what you need:
import { defaultConfig } from "@lightninglabs/wavelength-web";
defaultConfig("signet");
defaultConfig("signet", { dataDir: "my-wallet" });There is no regtest preset (local ports vary per machine); build that config by hand with your stack's endpoints and the insecure-transport flags:
import type { RuntimeConfig } from "@lightninglabs/wavelength-web";
const config: RuntimeConfig = {
network: "regtest",
arkServerAddress: "http://127.0.0.1:7071",
walletEsploraUrl: "http://127.0.0.1:8501",
swapServerAddress: "http://127.0.0.1:10032",
arkServerInsecure: true,
swapServerInsecure: true,
};Every field is documented on the RuntimeConfig
type. On web, arkServerAddress and swapServerAddress are REST URLs;
walletEsploraUrl is an HTTP Esplora endpoint on every platform. mainnet has
no public preset yet, so like regtest it is built by hand: supply the endpoints
and allowMainnet: true yourself.
The wasm runtime ships as a set of files (RUNTIME_ASSET_FILES) that make up the
in-browser wallet. Host them together at one base URL and point runtimeBaseUrl
at it:
import { RUNTIME_ASSET_FILES } from "@lightninglabs/wavelength-web";
// → wavewalletdk.wasm.gz, wasm_exec.js, sqlite-*.js, …You host the asset set yourself. Obtain it from the
wavelength release assets,
or build it from a wavelength checkout. See
Hosting runtime assets
for the exact steps.