@solutiofi/connectorkit-svelte
v0.2.1
Published
Solana wallet connection library for Svelte 5, powered by @solana/connector headless API
Readme
connectorkit-svelte
The ultimate Solana wallet connection library for Svelte 5. Powered by @solana/connector headless API and built with Svelte Runes.
Features
- ⚡ Zero-Config Polyfills - Works with Vite/SvelteKit out of the box (no
vite-plugin-node-polyfillsneeded!) - 🎨 Fully Themeable - Customize every color with CSS variables
- 📱 Mobile-First - Deep links to wallet apps + WalletConnect QR codes
- 🔧 Svelte 5 Runes - Built from the ground up for the new reactivity model
- 🌐 Wallet Standard - Supports all modern Solana wallets (Phantom, Solflare, Backpack, etc.)
- ✍️ Signing - Sign messages and transactions with full TypeScript support
- 💰 Balances - Auto-fetching for SOL and SPL tokens
- 🎯 Filtering - precise control over which wallets are shown, hidden, or featured
- 👥 Multi-Account - seamless switching between multiple accounts in a single wallet
Installation
npm install connectorkit-svelte @solana/connector @solana/web3.js
# or
pnpm add connectorkit-svelte @solana/connector @solana/web3.jsQuick Start
- Wrap your application with
SvelteConnectorProvider:
<!-- +layout.svelte -->
<script lang="ts">
import { SvelteConnectorProvider, WalletButton } from 'connectorkit-svelte';
const config = {
appName: 'My Solana App',
network: 'mainnet',
projectId: 'YOUR_WALLETCONNECT_ID' // Optional
};
</script>
<SvelteConnectorProvider {config}>
<nav>
<WalletButton />
</nav>
<slot />
</SvelteConnectorProvider>- Use the context in valid child components:
<!-- +page.svelte -->
<script lang="ts">
import { getConnectorContext, TokenList } from 'connectorkit-svelte';
const ctx = getConnectorContext();
</script>
{#if ctx.state.isConnected}
<h1>Welcome, {ctx.state.publicKey}</h1>
<TokenList />
{/if}Configuration
Detailed configuration reference for SvelteConnectorConfig:
interface SvelteConnectorConfig {
// --- Basic ---
appName: string; // Name shown in wallet connection prompts
appUrl?: string; // URL of your app (for wallet metadata)
network?: 'mainnet' | 'devnet' | 'testnet' | 'localnet'; // Default: 'mainnet'
// --- Connection ---
autoConnect?: boolean; // Attempt to auto-connect on load (default: false)
rpcUrl?: string; // Custom RPC URL (defaults to public Alchemy endpoint)
debug?: boolean; // Enable verbose logging
// --- UI/UX ---
walletOrder?: string[]; // Sort wallets by name: ['Phantom', 'Backpack']
walletLimit?: number; // Max wallets to show before "More" (default: 4)
imageProxy?: string; // Proxy URL for wallet icons to avoid CORS issues
// --- Filtering ---
wallets?: {
allowList?: string[]; // Strict list: ONLY show these wallets
denyList?: string[]; // Blacklist: NEVER show these wallets
featured?: string[]; // Highlight these wallets at the top with a badge
};
// --- Network Switching ---
clusters?: { // Custom networks for ClusterSelector
name: string;
id: string;
rpcUrl: string;
wsUrl?: string;
}[];
// --- WalletConnect (Optional) ---
walletConnect?: {
projectId: string; // Get from https://cloud.walletconnect.com
metadata?: {
name: string;
description: string;
url: string;
icons: string[];
};
};
}Component Reference
<WalletButton />
The primary connect button. Automatically handles "Connect", "Disconnect", "Copy Address", and "Change Wallet".
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| size | 'sm' \| 'md' \| 'lg' | 'md' | Scaling of the button |
| showIcon | boolean | true | Show wallet icon when connected |
| class | string | '' | Custom classes |
<WalletModal />
A standalone modal component. Use this if you want to create your own trigger button.
<script>
let isOpen = $state(false);
</script>
<button onclick={() => isOpen = true}>Connect Custom</button>
<WalletModal bind:open={isOpen} />| Prop | Type | Default | Description |
|------|------|---------|-------------|
| open | boolean | false | Control modal visibility |
| title | string | 'Connect Wallet' | Modal title text |
| onclose | () => void | - | Called when modal closes |
| footer | Snippet | - | Custom footer content (see below) |
Custom Footer Slot
You can pass a custom footer to add additional functionality like Ledger or hardware wallet connections:
<WalletModal bind:open={isOpen}>
{#snippet footer()}
<button onclick={handleLedgerConnect}>
🔒 Connect Ledger
</button>
{/snippet}
</WalletModal><ClusterSelector />
A dropdown to switch between networks (Mainnet/Devnet/Testnet) or custom clusters.
Multi-Account Support
If a connected wallet provides multiple accounts, connectorkit-svelte automatically handles them. The <WalletButton /> and <WalletDropdown /> include a built-in UI for switching accounts.
The selected account is persisted per-wallet, so users don't need to re-select when they reconnect.
<TokenList />
Displays a list of SPL tokens held by the connected wallet.
- Auto-fetches from RPC
- Shows token icons, symbols, and formatted balances
- Filters out zero-balance tokens
<AddressDisplay />
Displays a truncated wallet address with click-to-copy.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| address | string | required | The address to display |
| chars | number | 4 | Chars to show (e.g. ABCD...WXYZ) |
| copyable | boolean | false | Enable copy-to-clipboard |
Hooks & API
Access the connector state reactively anywhere in your app:
import { getConnectorContext } from 'connectorkit-svelte';
const ctx = getConnectorContext();State Properties
ctx.state.isConnected:booleanctx.state.publicKey:string | nullctx.state.allAccounts:WalletAccount[](All available accounts)ctx.state.isConnecting:booleanctx.state.error:WalletError | nullctx.currentCluster:ClusterConfigused for network switchingctx.currentRpcUrl:stringcurrent RPC endpoint
Methods
ctx.connect(walletId): Connect to a specific walletctx.disconnect(): Disconnect current walletctx.signMessage(message): Sign a byte arrayctx.signTransaction(tx): Sign a serialized transactionctx.signAndSendTransaction(tx, options): Sign and submit to networkctx.selectCluster(id): Switch networkctx.selectAccount(address): Switch active account
Theming
All styles are defined as CSS custom properties. Light mode is the default; add .dark class to body for dark mode.
Complete CSS Variable Reference
| Variable | Light Mode | Dark Mode | Description |
|----------|------------|-----------|-------------|
| Brand Colors ||||
| --sck-primary | #008AFF | #3DA6FF | Main action color |
| --sck-primary-hover | #0075D9 | #008AFF | Hover state |
| Backgrounds ||||
| --sck-bg | #FBFCFD | #191C21 | Modal/dropdown background |
| --sck-bg-secondary | #F4F5F7 | #272C33 | Card backgrounds |
| --sck-bg-hover | #E6E7EB | #30363F | Hover backgrounds |
| Text ||||
| --sck-text | #1F2329 | #FBFCFD | Primary text |
| --sck-text-muted | #5B6778 | #939EAE | Muted text |
| Borders ||||
| --sck-border | #CBD1D8 | #383F4A | Border color |
| Status Colors ||||
| --sck-success | #248C3E | #34D058 | Success states |
| --sck-error | #E42E2C | #F85149 | Error states |
| --sck-warning | #FFA500 | #FFCC00 | Warning states |
Enabling Dark Mode
Add the .dark class to the body element to activate dark mode:
<script>
import { onMount } from 'svelte';
let isDark = $state(true);
onMount(() => {
// Restore from localStorage or use system preference
const saved = localStorage.getItem('theme');
isDark = saved ? saved === 'dark' : window.matchMedia('(prefers-color-scheme: dark)').matches;
applyTheme();
});
function toggleTheme() {
isDark = !isDark;
localStorage.setItem('theme', isDark ? 'dark' : 'light');
applyTheme();
}
function applyTheme() {
document.body.classList.toggle('dark', isDark);
}
</script>
<button onclick={toggleTheme}>
{isDark ? '☀️' : '🌙'}
</button>Troubleshooting & FAQ
"Global is not defined" or "Buffer is not defined"?
You generally do not need to configure this manually. connectorkit-svelte automatically polyfills global, Buffer, and process in the browser environment when the provider mounts.
Does this work with SvelteKit SSR?
Yes. The library checks for window before running any wallet logic. However, you should ensure <SvelteConnectorProvider> is only rendered in the client or handles checking browser from $app/environment if you run into hydration mismatches (though we handle most cases internally).
My wallet isn't showing up?
- Ensure the wallet is installed and supports the Wallet Standard (most do).
- Check if you have
wallets.allowListconfigured and the wallet is missing from it. - Check
wallets.denyList.
Mobile wallets aren't detected?
On mobile Safari/Chrome, wallet extensions can't inject—wallets are standalone apps. connectorkit-svelte handles this automatically:
Native Mobile App Support (Automatic):
- On mobile devices, wallets like Phantom, Solflare, Backpack, Jupiter, and Magic Eden are automatically detected and added to the wallet list.
- They appear as standard wallets in the connect modal.
- Tapping them initiates a secure Deep Link connection (redirects to the wallet app).
- After approval, the wallet redirects back to your dApp, and the connection is automatically established.
WalletConnect (recommended as fallback): Configure WalletConnect for universal mobile support:
const config = {
appName: 'My App',
network: 'mainnet',
walletConnect: {
projectId: 'YOUR_PROJECT_ID', // Free at https://cloud.walletconnect.com
metadata: {
name: 'My App',
description: 'My Solana dApp',
url: 'https://myapp.com',
icons: ['https://myapp.com/icon.png']
}
}
};Mobile connection flow:
- User opens your dApp in mobile Safari/Chrome
- Taps "Connect Wallet"
- Sees Phantom, Solflare, etc. in the standard wallet list
- Taps "Phantom" -> Redirects to Phantom app -> User Connects
- Redirects back to your dApp -> Wallet is designated as "Connected"
Pro tip: If users open your dApp from within a wallet's browser (e.g., Phantom's built-in browser), wallets inject automatically—no deep links needed.
License
MIT © 2026 Connectorkit Authors
