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.

Share
devrels.xyz/a/75short link

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.

Keep reading

Get new articles in your inbox

Technical deep-dives on Solana tooling, infrastructure, and ecosystem. No noise.

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