Skip to content

konekt-ui

konekt-ui is an optional React interface for Konekt. It can list compatible wallets, show the pairing QR, open wallet links, and report connection errors.

Choose an entry point:

Import Use it when
konekt-ui You have a Konekt Provider. Works with every configured namespace and does not require wagmi.
konekt-ui/wagmi Your EVM app already manages connectors and account state with wagmi.
konekt-ui/wallet-standard Your Solana app should list injected extensions (Phantom, Solflare, Backpack) next to WalletConnect pairing.
konekt-ui/cosmos Your Cosmos app should list Keplr-API extensions (Keplr, Leap) next to WalletConnect pairing.

The components require React 18 or newer. The wagmi entry point also requires wagmi 2 or 3 and viem 2; the wallet-standard and cosmos entry points need only React.

Konekt UI is better when the app needs a wallet picker, pairing QR, and account controls without adopting a full onboarding platform.

UI path First load Overall
Vite app with Konekt WalletModal 19.06 kB 45.52 kB
Vite app with @reown/appkit@1.8.23 721.26 kB 1079.28 kB

Those rows are production builds of packages/size-konekt-ui and packages/size-appkit, with React marked external. The Konekt modal and stylesheet alone are 13.28 kB (10.30 kB JavaScript and 2.98 kB CSS); the wagmi ConnectButton path is 14.87 kB with the same stylesheet. AppKit remains a broader product, but even with email, socials, swaps, on-ramp, and analytics disabled it still first-loads wallet-list and email UI.

Capability Konekt UI Reown AppKit
Wallet picker and pairing QR Yes Yes
Mobile wallet links Yes Yes
Light, dark, and system themes Yes Yes
App-owned styling and unstyled mode Yes Theming APIs
Optional wagmi account, network, and disconnect controls Yes Yes
Embedded email and social wallets No Yes
Smart accounts No Yes
Built-in swaps and on-ramp No Yes

That narrower scope is the advantage for apps that already own authentication, transactions, RPC access, and visual design. Konekt UI does not make those applications download or configure unrelated product features. AppKit is the better fit only when the app wants its broader onboarding and transaction suite.

Terminal window
pnpm add konekt konekt-ui react

Import the default stylesheet once near your app’s entry point:

import "konekt-ui/styles.css";

Skip the stylesheet only when you plan to use the unstyled option and supply all component styles yourself.

useProviderPairing() adapts a Konekt provider to the state and actions required by WalletModal:

import { useState } from "react";
import type { Provider } from "konekt";
import { useProviderPairing, WalletModal } from "konekt-ui";
import "konekt-ui/styles.css";
export function WalletConnection({ provider }: { provider: Provider }) {
const [open, setOpen] = useState(false);
const pairing = useProviderPairing(provider);
return (
<>
<button type="button" onClick={() => setOpen(true)}>
Connect wallet
</button>
<WalletModal
open={open}
pairing={pairing}
onClose={() => setOpen(false)}
/>
</>
);
}

The pairing carries the provider’s WalletConnect project ID, and the modal sends it to the WalletConnect Explorer when loading wallet listings — there is no separate ID to pass.

On a desktop browser, pairing does not begin when the modal opens. It begins when the user picks a wallet or the WalletConnect option and reaches the QR view. From there the modal:

  1. calls provider.connect({ signal });
  2. renders the URI from display_uri;
  3. replaces a pairing that is about to lapse with a fresh one, calling onDismiss for the discarded attempt;
  4. aborts the pending connection and calls onDismiss if the user leaves before it finishes;
  5. closes itself once the provider connects, by calling onClose.

Because it closes itself, keep open as controlled state and let onClose set it to false. The modal also skips pairing entirely when pairing.connected is already true.

A phone gets a different flow, and the difference is not cosmetic. WebKit refuses to leave for a wallet’s custom scheme once the tap that asked for it has expired, and a pairing URI takes a relay round trip to arrive — so a modal that pairs on tap can never deep link on iOS. The modal therefore:

  • pairs as soon as it opens, so a URI is in hand before the user chooses;
  • leaves for the wallet inside the tap itself, and shows “Continue in Wallet” with an Open button rather than a QR code nobody can scan with the phone they are holding;
  • lists only wallets that advertised a mobile link, because a desktop-only listing cannot be reached from a phone;
  • opens automatically once per chosen wallet. A replaced pairing waits to be asked, so the page never navigates away on its own.

Pairing early means a socket opens for a modal the user may only browse. That is the price of the redirect working at all.

On its own, the provider adapter lists WalletConnect Explorer wallets and the generic QR option. Pass sources to also list injected browser wallets (see Injected wallets), or use the wagmi adapter when wagmi already manages your EVM connectors.

Prop Type Purpose
open boolean Whether the dialog renders. Required.
pairing Pairing From useProviderPairing() or useWagmiPairing(). Required. Carries the project ID for Explorer queries.
onClose () => void Asks the parent to set open to false. Required.
chains readonly string[] CAIP-2 IDs used to filter Explorer results. Defaults to the provider’s chains.
wallets WalletFilter include, exclude, and featured Explorer IDs.
onDismiss () => void Runs when an unfinished pairing is discarded: the user left, or it was replaced before lapsing.
theme "light" | "dark" | "system" Color scheme. Defaults to "system".
className string Extra class on the root.
style WcStyle Inline styles plus --kui-* token overrides.
unstyled boolean Drops the default kui-* classes, keeping data-kui attributes.

className, style, theme, and unstyled are shared by every konekt-ui component.

wagmi remains the path for EVM apps: it already discovers injected EVM wallets and owns their account state, so konekt-ui only mirrors its connectors. Solana and Cosmos have no wagmi. For them, useProviderPairing accepts sources — discovery hooks whose wallets appear as installed choices next to WalletConnect pairing:

Import Ecosystem Discovery
konekt-ui/wallet-standard Solana Wallet Standard announce events (Phantom, Solflare, Backpack)
konekt-ui/cosmos Cosmos Probes window.keplr-shaped extensions (Keplr, Leap)

konekt-ui/wallet-standard is for Solana. The underlying announce protocol is chain-agnostic, but this entry point lists only wallets that serve solana: chains unless you pass an explicit chains filter.

Sources are discovery only. Connecting an injected wallet never touches the Konekt provider, and after onConnect the app owns the wallet handle: accounts, signing, and disconnects come from that handle, not from the modal.

import { useState } from "react";
import type { Provider } from "konekt";
import { useProviderPairing, WalletModal } from "konekt-ui";
import { type CosmosInjectedWallet, useCosmosSource } from "konekt-ui/cosmos";
import { useWalletStandardSource, type WalletStandardWallet } from "konekt-ui/wallet-standard";
export function MultiChainConnection({ provider }: { provider: Provider }) {
const [open, setOpen] = useState(false);
const [solanaWallet, setSolanaWallet] = useState<WalletStandardWallet>();
const [cosmosWallet, setCosmosWallet] = useState<CosmosInjectedWallet>();
const solana = useWalletStandardSource({ onConnect: setSolanaWallet });
const cosmos = useCosmosSource({ chainIds: ["cosmoshub-4"], onConnect: setCosmosWallet });
const pairing = useProviderPairing(provider, { sources: [solana, cosmos] });
return (
<>
<button type="button" onClick={() => setOpen(true)}>
Connect wallet
</button>
<WalletModal open={open} pairing={pairing} onClose={() => setOpen(false)} />
{solanaWallet && <p>Solana: {solanaWallet.accounts[0]?.address}</p>}
{cosmosWallet && <p>Cosmos wallet enabled.</p>}
</>
);
}

After onConnect, sign with the handle’s own API: the Wallet Standard wallet exposes features such as solana:signMessage and solana:signTransaction, and the Keplr handle offers offline signers for CosmJS directly. Injected wallets do not need the signing bridges from the Solana and CosmJS guides — those exist only for signing over a Konekt session.

Option Type Purpose
onConnect (wallet: WalletStandardWallet) => void Receives the connected wallet. Required.
chains readonly string[] Wallet Standard chain ids a wallet must serve, e.g. "solana:mainnet". Defaults to any solana: chain. These are Wallet Standard network names, not the genesis-hash CAIP-2 ids Konekt chains use.
onError (error: Error) => void Receives connect failures, e.g. a dismissed extension prompt.
Option Type Purpose
chainIds readonly string[] Cosmos chain ids passed to enable, e.g. ["cosmoshub-4"]. Required.
onConnect (wallet: CosmosInjectedWallet) => void Receives the enabled wallet. Required.
onError (error: Error) => void Receives enable failures.

A source is a plain object, so an app can supply discovery konekt-ui does not ship — for example EIP-6963 announcements in a vanilla EVM app that does not use wagmi:

import type { LocalWalletSource } from "konekt-ui";
declare const eip6963Wallets: LocalWalletSource["wallets"];
const injectedEvm: LocalWalletSource = {
wallets: eip6963Wallets,
connect: (wallet) => {
// request accounts on the announced provider and keep the handle
},
connected: false,
};

Each source owns its wallets: the modal routes a clicked wallet back to the source whose wallets contains it, and a source turning connected closes the modal.

Install the optional peers (React 18+, wagmi 2 or 3, viem 2):

Terminal window
pnpm add konekt konekt-ui react viem wagmi

ConnectButton uses the connectors already registered in your wagmi config:

  • a connector whose id or type is "konekt" provides WalletConnect pairing;
  • other connectors appear as installed wallet choices, injected ones only while their provider is in the browser: a config registers injected() whether or not an extension answers, and mobile Safari usually has none;
  • a named EIP-6963 entry hides the generic injected connector, so one wallet is one row;
  • after connection, the button opens account, network, and disconnect controls.
import { ConnectButton } from "konekt-ui/wagmi";
import "konekt-ui/styles.css";
export function WalletControls() {
return <ConnectButton />;
}

The wagmi entry point also exports the connector: register konekt(options) from konekt-ui/wagmi in createConfig(). It delays Provider.init() until first use, so static registration does not open a relay socket. The complete setup is in the wagmi integration guide.

Prop Type Purpose
chains readonly string[] CAIP-2 IDs for wallet filtering. Defaults to the configured wagmi chains.
wallets WalletFilter include, exclude, and featured Explorer IDs.
getWalletConnect () => Promise<Connector> Supplies the Konekt connector when the wagmi config does not already contain one.
projectId string Explorer queries, only with getWalletConnect — a registered Konekt connector supplies its own.
onDismiss () => void Cancels connector-owned pairing work when the user closes the modal.

It also accepts the shared theme, className, style, and unstyled props.

Three of these cover the less common cases:

  • getWalletConnect is a ConnectButton prop (and a useWagmiPairing() option) that returns the WalletConnect connector on demand, for apps that keep it out of createConfig() so a visitor who never connects never loads Konekt. Pass projectId alongside it, because there is no registered connector to read the ID from until pairing starts. See the wagmi guide for the trade-off it carries.
  • onDismiss runs when the user closes the modal, so connector-owned work can be cancelled alongside the pairing.
  • useWagmiPairing() gives you the same pairing state without ConnectButton, for a custom trigger rendered with WalletModal.

By default, WalletModal asks the Explorer for wallets that support one of the provider’s configured chains. Override that list with CAIP-2 IDs:

import { useState } from "react";
import type { Provider } from "konekt";
import { useProviderPairing, WalletModal } from "konekt-ui";
// Copy the IDs from https://walletconnect.com/explorer
const featuredWalletIds = ["", ""];
const hiddenWalletIds = [""];
export function WalletPicker({ provider }: { provider: Provider }) {
const [open, setOpen] = useState(false);
const pairing = useProviderPairing(provider);
return (
<WalletModal
open={open}
pairing={pairing}
onClose={() => setOpen(false)}
chains={["eip155:1", "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"]}
wallets={{ featured: featuredWalletIds, exclude: hiddenWalletIds }}
/>
);
}

Wallet filter values are WalletConnect Explorer IDs, not connector IDs or reverse-domain names.

Filter Effect
include Show only these Explorer wallets.
featured Put these wallets on the modal’s first screen.
exclude Remove these wallets from Explorer results as each page loads.

Filters do not hide installed wagmi connectors. Control those in your wagmi configuration.

The default theme="system" follows the user’s operating-system color scheme. Pass theme="light" or theme="dark" to lock it.

Override design tokens through style:

<WalletModal
open={open}
pairing={pairing}
onClose={() => setOpen(false)}
theme="dark"
style={{
"--kui-accent": "#7c5cff",
"--kui-radius": "20px",
}}
/>

Pass unstyled to remove default kui-* classes. Stable data-kui and data-kui-slot attributes remain for your selectors.

When supplying custom styles, preserve visible keyboard focus, sufficient color contrast, the QR code’s square dimensions, and a clear error state.

The shared modal component:

  • moves focus into the dialog when it opens;
  • keeps Tab focus inside the dialog;
  • closes on Escape or backdrop activation;
  • restores focus to the previously focused element;
  • exposes the dialog title and control labels to assistive technology.

If you compose the lower-level Modal or QrCode exports yourself, provide concise visible instructions alongside them. A QR code alone is not enough for someone who cannot scan it; offer a wallet link or copy action when possible.

WalletModal is one arrangement of smaller exports. Use them directly when you need a different one.

Export From Purpose
Modal konekt-ui The accessible dialog shell: focus trap, Escape, backdrop, restored focus.
QrCode konekt-ui Renders a pairing URI as a QR code.
Avatar konekt-ui Address-derived gradient disc used by the account chip.
truncateAddress konekt-ui Shortens a hex address for a chip or heading.
fetchWallets konekt-ui Queries the WalletConnect Explorer. Returns one page of listings.
filterWallets konekt-ui Applies include, exclude, and featured to listings.
FEATURED_WALLET_IDS konekt-ui Default featured Explorer IDs.
walletLink konekt-ui The base URL a listing advertised for one platform, or nothing.
walletHref konekt-ui Builds a wallet deep link from a listing and a pairing URI.
openWalletLink konekt-ui Navigates to a wallet link. Call it inside the tap that asked for it.
isMobile konekt-ui Whether to prefer deep links over a QR code.
pairingExpiry konekt-ui The deadline a pairing URI carries, in unix seconds.
pairingRefreshDelay konekt-ui How long that URI may still be offered, in milliseconds.
AccountModal konekt-ui/wagmi The connected account and network dialog ConnectButton opens.

AccountModal is controlled through open, view ("account" or "networks"), onView, and onClose, so a custom button can reuse the account and network switching UI without ConnectButton.