All articles
solanakitsveltekitsvelte

Kit-squared: @solana/kit in SvelteKit, no React needed

@solana/kit is framework-agnostic. Here's how to wire it into a SvelteKit app — RPC store, wallet store, reactive subscriptions, no React-only assumptions.

Most Solana JS tutorials assume React. @solana/kit deliberately doesn't — it's framework-agnostic functional code, no React adapters, no hooks. That makes it a clean fit for SvelteKit, where the reactivity model is the framework's job, not the SDK's.

This article is the integration pattern.

Why kit is the right base for SvelteKit

  • No React. Classic web3.js was OK in Svelte but you always felt the React shape in its class-based API. Kit is just functions.
  • SSR-safe. No hidden globals, nowindow assumptions. The RPC client works in server load functions and in onMount.
  • Tree-shakeable. SvelteKit's build dead-code-eliminates aggressively; kit's per-program packages let you ship a ~25KB Solana bundle.
  • Bigint-everywhere. Svelte stores hold bigints without complaint; no BN.js overhead.

RPC as a Svelte store

typescript
// src/lib/solana/rpc.ts
import { writable, readable, type Readable } from "svelte/store"
import {
  createSolanaRpc,
  createSolanaRpcSubscriptions,
  type Address,
  type Slot,
} from "@solana/kit"

const HTTP = import.meta.env.PUBLIC_SOLANA_RPC ?? "https://api.mainnet-beta.solana.com"
const WS   = HTTP.replace(/^http/, "ws")

export const rpc  = createSolanaRpc(HTTP)
export const subs = createSolanaRpcSubscriptions(WS)

// A reactive store backed by an RPC subscription
export function slotStore(): Readable<Slot | null> {
  return readable<Slot | null>(null, (set) => {
    const ac = new AbortController()
    ;(async () => {
      const notifications = await subs.slotNotifications().subscribe({ abortSignal: ac.signal })
      for await (const n of notifications) set(n.slot)
    })()
    return () => ac.abort()
  })
}

export function accountStore(address: Address) {
  return readable<{ lamports: bigint; data: Uint8Array } | null>(null, (set) => {
    const ac = new AbortController()
    ;(async () => {
      // initial fetch
      const { value } = await rpc.getAccountInfo(address).send()
      set(value as any)

      // subscribe to changes
      const notifications = await subs.accountNotifications(address).subscribe({
        abortSignal: ac.signal,
      })
      for await (const n of notifications) set(n.value as any)
    })()
    return () => ac.abort()
  })
}

Each store wraps a kit subscription in Svelte's store contract. The cleanup function fires when the last subscriber unsubscribes, which aborts the RPC subscription. No leaks.

Wallet adapter for Svelte

For Phantom / Solflare / Backpack connections in a SvelteKit app, use @wallet-standard/appdirectly — wallet standard is framework-agnostic, no React adapter required:

typescript
// src/lib/solana/wallet.ts
import { writable } from "svelte/store"
import { getWallets, type Wallet } from "@wallet-standard/app"

export const wallets = writable<Wallet[]>([])
export const selected = writable<Wallet | null>(null)
export const account = writable<{ address: string; publicKey: Uint8Array } | null>(null)

if (typeof window !== "undefined") {
  const { get, on } = getWallets()

  // Snapshot
  wallets.set(get().filter((w) => w.chains.includes("solana:mainnet")))

  // Stay in sync with wallet installs/removals
  on("register", () => wallets.set(get()))
  on("unregister", () => wallets.set(get()))
}

export async function connect(wallet: Wallet) {
  const standardFeature = wallet.features["standard:connect" as const]
  if (!standardFeature) throw new Error("wallet doesn't implement standard:connect")
  const result = await (standardFeature as any).connect()
  selected.set(wallet)
  account.set(result.accounts[0])
}

SSR-safe load functions

typescript
// src/routes/+page.server.ts
import { rpc } from "$lib/solana/rpc"
import { address } from "@solana/kit"

export async function load({ params }) {
  // Runs on the server — no window, no wallet, just RPC
  const balance = await rpc.getBalance(address(params.addr)).send()
  return { addressBalance: balance.value }
}
svelte
<!-- src/routes/+page.svelte -->
<script lang="ts">
  import { slotStore } from "$lib/solana/rpc"

  export let data

  const slot = slotStore()  // reactive store of current slot
</script>

<h1>Balance: {data.addressBalance} lamports</h1>
<p>Current slot: {$slot ?? "loading"}</p>

Sending a tx from Svelte

svelte
<script lang="ts">
  import { account, selected } from "$lib/solana/wallet"
  import { rpc } from "$lib/solana/rpc"
  import {
    pipe, createTransactionMessage, setTransactionMessageFeePayer,
    setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstruction,
    address, lamports,
  } from "@solana/kit"
  import { getTransferSolInstruction } from "@solana-program/system"

  async function send() {
    if (!$account || !$selected) return

    const ix = getTransferSolInstruction({
      source: $account.address as any,
      destination: address("RecipientPubkey…"),
      amount: lamports(10_000_000n),
    })

    const { value: bh } = await rpc.getLatestBlockhash().send()
    const message = pipe(
      createTransactionMessage({ version: 0 }),
      (m) => setTransactionMessageFeePayer($account!.address as any, m),
      (m) => setTransactionMessageLifetimeUsingBlockhash(bh, m),
      (m) => appendTransactionMessageInstruction(ix, m),
    )

    // Hand the encoded message to the wallet to sign + send
    const signFeature = $selected.features["solana:signAndSendTransaction"]
    const result = await (signFeature as any).signAndSendTransaction({
      account: $account,
      transaction: /* encode message to wire format */ ,
      chain: "solana:mainnet",
    })

    console.log("sig:", result[0].signature)
  }
</script>

<button on:click={send}>Send 0.01 SOL</button>

The case for SvelteKit + Solana

SvelteKit has aggressive bundle optimisation, real SSR (load functions, +page.server.ts), and a smaller runtime than Next.js. Combined with kit's tree shaking, the result is a Solana dapp that ships dramatically less JavaScript than the Next.js/React equivalent. For consumer apps where time-to- interactive matters — gaming, social, retail trading — that gap is real.

References

Kit + SvelteKit is the lightest Solana stack you can ship in 2026. The framework does reactivity, kit does Solana, neither carries assumptions about the other.

Kit-squared: @solana/kit in SvelteKit, no React needed | devrels.xyz