@solana/kit: the modern JS client, functional + tree-shakeable
@solana/kit replaces the classic web3.js with a functional, tree-shakeable, strict-typed client. Here's the primitive shape and the migration patterns.
@solana/kit (formerly @solana/web3.js v2) is the official successor to the classic @solana/web3.js client. It's not a refresh — it's a ground-up redesign around functional composition, branded types, and tree shaking. A swap-only bundle drops from ~250KB (classic web3.js) to ~25KB (kit).
The trade: more verbose, more types, more explicit. Here's the design as it actually is.
Branded types instead of strings
In classic web3.js, a Solana address was a PublicKey class. In kit, it's a branded primitive — a string with a compile-time tag that distinguishes it from a regular string:
import type { Address } from "@solana/kit"
import { address } from "@solana/kit"
// Address is a string brand. You can't pass a raw string where an
// Address is expected — TypeScript catches it at compile time.
const usdc: Address = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
// At runtime, it's a string. No class, no method overhead.
console.log(typeof usdc) // "string"
console.log(usdc) // "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"Same pattern for Signature, Blockhash, Slot, Lamports, etc — each is a compile-time-checked brand on a primitive value.
Functional composition with pipe
import {
createSolanaRpc,
createKeyPairSignerFromBytes,
pipe,
createTransactionMessage,
setTransactionMessageFeePayer,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstruction,
signAndSendTransactionMessageWithSigners,
lamports,
} from "@solana/kit"
import { getTransferSolInstruction } from "@solana-program/system"
const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com")
const signer = await createKeyPairSignerFromBytes(secretBytes)
const ix = getTransferSolInstruction({
source: signer,
destination: recipient,
amount: lamports(10_000_000n),
})
const { value: bh } = await rpc.getLatestBlockhash().send()
const tx = pipe(
createTransactionMessage({ version: 0 }),
(m) => setTransactionMessageFeePayer(signer.address, m),
(m) => setTransactionMessageLifetimeUsingBlockhash(bh, m),
(m) => appendTransactionMessageInstruction(ix, m),
)
const sig = await signAndSendTransactionMessageWithSigners(tx)Each step is a pure function: takes a message, returns a new message. No mutation, no this, every step independently importable and tree-shakeable.
Codecs for serialization
Kit ships @solana/codecs, a typed binary codec library. Use it to serialize / deserialize account data without hand-rolling Borsh:
import {
getStructCodec, getU64Codec, getBoolCodec, getAddressCodec,
} from "@solana/kit"
const VaultCodec = getStructCodec([
["owner", getAddressCodec()],
["balance", getU64Codec()],
["is_active", getBoolCodec()],
])
// Encode an object → Uint8Array
const bytes = VaultCodec.encode({ owner: someAddress, balance: 1000n, is_active: true })
// Decode Uint8Array → typed object
const vault = VaultCodec.decode(accountInfo.data)
console.log(vault.balance) // bigintEach codec is composable and bidirectional. Anchor IDLs generate codec definitions for free via codama.
Per-program instruction packages
Where classic web3.js bundled every program's instruction builders inside the main package, kit ships them as separate packages — install only what you use:
npm install @solana/kit # core
npm install @solana-program/system # SystemProgram (transfer, createAccount, …)
npm install @solana-program/token # SPL Token instructions
npm install @solana-program/token-2022 # Token-2022 instructions
npm install @solana-program/compute-budget # ComputeBudgetProgram
npm install @solana-program/address-lookup-table # ALT instructions
npm install @solana-program/memo # Memo programYour transfer-only bundle pulls in @solana/kit + @solana-program/system and stops there. ~25KB gzipped.
The rpc object
Every JSON-RPC method maps to a chainable call returning a promise of a typed response:
const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com")
// .send() actually fires the call; everything before is a builder
const slot = await rpc.getSlot().send()
const balance = await rpc.getBalance(myAddress).send()
const accountInfo = await rpc.getAccountInfo(myAddress, { encoding: "base64" }).send()
const programAccts = await rpc.getProgramAccounts(programId, {
filters: [{ dataSize: 165 }],
encoding: "base64",
}).send()
// Subscriptions are a separate transport
import { createSolanaRpcSubscriptions } from "@solana/kit"
const subs = createSolanaRpcSubscriptions("wss://api.mainnet-beta.solana.com")
const notifications = await subs.accountNotifications(myAddress).subscribe({ abortSignal })
for await (const update of notifications) {
console.log("account changed:", update)
}Why bigint everywhere
Lamports, slots, supply, balances — all bigint in kit. JavaScript's Number can't safely represent values past 2^53 - 1; Solana's u64 amounts blow past that for large balances. Kit's bigint-everywhere policy prevents the silent truncation that used to plague classic web3.js code.
Migration cheat sheet
Classic web3.js → @solana/kit
─────────────────────────────────────────────────────────────────
new Connection(url) createSolanaRpc(url)
new PublicKey(str) address(str) (or just the Address brand)
keypair.publicKey signer.address
LAMPORTS_PER_SOL lamports(1_000_000_000n)
new Transaction().add(ix) pipe(createTransactionMessage(…), appendInstruction(ix, …))
conn.sendTransaction(tx, signers) signAndSendTransactionMessageWithSigners(tx)
SystemProgram.transfer(…) getTransferSolInstruction(…) from @solana-program/system
conn.getAccountInfo(pk) rpc.getAccountInfo(addr, opts).send()
conn.onAccountChange(pk, cb) subscribe via createSolanaRpcSubscriptionsReferences
- anza-xyz/kit
- @solana/kit on npm
- Kit vs web3.js vs gill — comparison
- codama — auto-generate kit clients from IDLs
Kit isn't a drop-in replacement — it's a redesign. The functional shape, branded types, and tree shaking are the reasons it exists. If your bundle size, types, or RPC subscription story matters, the migration is worth it.