Mount the provider
Wrap your app with WhiskProvider and configure the wallet adapters it needs.
<WhiskProvider> is the only part of Whisk you have to set up by
hand. It builds the engine, mounts the wallet stack, and exposes
everything to the hooks and components below it. Once it's in place,
the rest of Whisk is just children of it.
The shortest version
"use client";
import { WhiskProvider, createWhiskConfig, evm } from "@usewhisk/react";
const config = createWhiskConfig({
wallets: [
evm({
projectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID!,
}),
],
chains: ["Arc_Testnet", "Base_Sepolia"],
});
export function Providers({ children }: { children: React.ReactNode }) {
return <WhiskProvider config={config}>{children}</WhiskProvider>;
}Then use it at the root of your tree:
import { Providers } from "./providers";
import "@usewhisk/react/styles.css";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}Wrap once, at the root. Don't sprinkle WhiskProvider around the
tree because the engine should be a singleton per app.
Mode (testnet / mainnet)
Whisk operates in one of two modes. The mode is inferred from your chain list — pass all-testnet chains and you're in testnet mode, all-mainnet chains and you're in mainnet mode. You can also set it explicitly:
createWhiskConfig({
mode: "testnet", // optional, inferred from chains
chains: ["Arc_Testnet", "Base_Sepolia"],
// ...
});Mode drives three visible behaviours:
- A "Testnet" pill at the top of the widget. Always visible in testnet mode, from the moment the card mounts. Mainnet renders no pill — absence is the safer signal that real money is moving.
- The default ENS resolver. Testnet mode queries Sepolia ENS
first, then falls back to mainnet ENS (so a dev who registers a
Sepolia name for integration testing gets it, while
vitalik.ethstill resolves to his mainnet address). Mainnet mode queries mainnet only. Customise viacreateDefaultResolver({ mode }). - Recovery persistence namespacing. Mid-flight failure snapshots
are keyed on
(mode, walletKind, address, sourceChain)— a testnet recovery cannot resurrect in a mainnet config, full stop.
Mixed configs (some mainnet chains, some testnet) almost always
indicate a copy-paste mistake. Whisk defaults to testnet for safety
in that case and logs a console warning so you notice.
Adding Solana
Drop the solana() adapter into the wallets array. Whisk only
mounts the Solana provider stack when this adapter is present, so
EVM-only apps don't pay for code they won't run.
import { createWhiskConfig, evm, solana } from "@usewhisk/react";
const config = createWhiskConfig({
wallets: [
evm({ projectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID! }),
solana({ network: "devnet" }),
],
chains: ["Arc_Testnet", "Base_Sepolia", "Solana_Devnet"],
});Reusing your existing wagmi setup
If your app already mounts <WagmiProvider>, Whisk detects it and
reuses your config. In that case the evm() adapter becomes
optional and so the host wagmi config becomes what drives EVM operations:
<WagmiProvider config={yourWagmiConfig}>
<QueryClientProvider client={yourQueryClient}>
<WhiskProvider config={createWhiskConfig({ chains: ["Base"] })}>
{children}
</WhiskProvider>
</QueryClientProvider>
</WagmiProvider>The same logic applies to @tanstack/react-query. An outer
<QueryClientProvider> gets reused; pass queryClient explicitly
to <WhiskProvider> only when you want to override the detection.
Pinning the theme
The default mode is "system". Whisk follows the OS via
prefers-color-scheme. To pin it instead, pass theme:
<WhiskProvider config={config} theme="dark">
{children}
</WhiskProvider>When pinned, the widget root renders data-whisk-theme="dark". The
selector lives at the bottom of the cascade so it beats the media
query.
See Theming for the full token contract.
