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 vs Reown AppKit
Section titled “Konekt UI vs Reown AppKit”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.
Install
Section titled “Install”pnpm add konekt konekt-ui reactImport 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.
Use WalletModal with a provider
Section titled “Use WalletModal with a provider”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.
What the modal does on its own
Section titled “What the modal does on its own”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:
- calls
provider.connect({ signal }); - renders the URI from
display_uri; - replaces a pairing that is about to lapse with a fresh one, calling
onDismissfor the discarded attempt; - aborts the pending connection and calls
onDismissif the user leaves before it finishes; - 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.
On a phone
Section titled “On a phone”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.
WalletModal props
Section titled “WalletModal props”| 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.
Injected wallets without wagmi
Section titled “Injected wallets without wagmi”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.
useWalletStandardSource options
Section titled “useWalletStandardSource options”| 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. |
useCosmosSource options
Section titled “useCosmosSource options”| 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. |
Writing your own source
Section titled “Writing your own source”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):
pnpm add konekt konekt-ui react viem wagmiConnectButton uses the connectors already registered in your wagmi config:
- a connector whose
idortypeis"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.
ConnectButton props
Section titled “ConnectButton props”| 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:
getWalletConnectis aConnectButtonprop (and auseWagmiPairing()option) that returns the WalletConnect connector on demand, for apps that keep it out ofcreateConfig()so a visitor who never connects never loads Konekt. PassprojectIdalongside 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.onDismissruns when the user closes the modal, so connector-owned work can be cancelled alongside the pairing.useWagmiPairing()gives you the same pairing state withoutConnectButton, for a custom trigger rendered withWalletModal.
Which wallets, which networks
Section titled “Which wallets, which networks”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/explorerconst 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.
Theme and custom styles
Section titled “Theme and custom styles”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.
Built-in dialog behavior
Section titled “Built-in dialog behavior”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.
Building your own picker
Section titled “Building your own picker”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.