Migrating From web3.js 1.x to @solana/kit
TL;DR
- - @solana/kit replaces @solana/web3.js with a modular, tree-shakeable architecture
- - The API is fundamentally different — not a drop-in replacement
- - Migrate incrementally: both packages can coexist during transition
The @solana/kit package (previously @solana/web3.js 2.0) is a complete rewrite of Solana’s JavaScript SDK. It replaces the monolithic @solana/web3.js 1.x with a modular, tree-shakeable set of packages. The API is different — this is not a minor version bump but a new architecture.
Why migrate
| Feature | web3.js 1.x | @solana/kit |
|---|---|---|
| Bundle size | ~300 KB | Tree-shakeable, often < 50 KB |
| Architecture | Monolithic class-based | Modular, functional |
| TypeScript | Basic types | Branded types, full type safety |
| Codecs | Built-in, limited | Composable, extensible |
| RPC types | Untyped responses | Fully typed RPC responses |
| Maintenance | Legacy, limited updates | Actively developed |
Key differences
Connection → RPC client
// web3.js 1.x
import { Connection } from "@solana/web3.js";
const connection = new Connection("https://api.mainnet-beta.solana.com");
const balance = await connection.getBalance(publicKey);
// @solana/kit
import { createSolanaRpc } from "@solana/kit";
const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");
const balance = await rpc.getBalance(address).send();
The RPC client returns a pending request that you explicitly .send(). This enables batching and request composition.
PublicKey → Address
// web3.js 1.x
import { PublicKey } from "@solana/web3.js";
const pubkey = new PublicKey("11111111111111111111111111111111");
// @solana/kit
import { address } from "@solana/kit";
const addr = address("11111111111111111111111111111111");
Addresses are branded strings, not class instances. They carry type-level validation.
Keypair → KeyPairSigner
// web3.js 1.x
import { Keypair } from "@solana/web3.js";
const keypair = Keypair.generate();
// @solana/kit
import { generateKeyPairSigner } from "@solana/kit";
const signer = await generateKeyPairSigner();
Signers are an abstraction that can represent keypairs, hardware wallets, or custodial services.
Transaction building
// web3.js 1.x
import { Transaction, SystemProgram } from "@solana/web3.js";
const tx = new Transaction().add(
SystemProgram.transfer({
fromPubkey: sender.publicKey,
toPubkey: recipient,
lamports: 1_000_000,
})
);
tx.recentBlockhash = blockhash;
tx.sign(sender);
// @solana/kit
import {
pipe,
createTransactionMessage,
setTransactionMessageFeePayer,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstruction,
signAndSendTransactionMessageWithSigners,
} from "@solana/kit";
import { getTransferSolInstruction } from "@solana-program/system";
const tx = pipe(
createTransactionMessage({ version: 0 }),
(msg) => setTransactionMessageFeePayer(signer.address, msg),
(msg) => setTransactionMessageLifetimeUsingBlockhash(blockhash, msg),
(msg) => appendTransactionMessageInstruction(
getTransferSolInstruction({
source: signer,
destination: address(recipient),
amount: 1_000_000n,
}),
msg,
),
);
await signAndSendTransactionMessageWithSigners(tx);
The pipe pattern makes transaction construction composable and type-safe at each step.
Amounts are bigint
// web3.js 1.x
const lamports = 1_000_000; // number
// @solana/kit
const lamports = 1_000_000n; // bigint (lamports)
All token and lamport amounts are bigint to prevent precision loss with large values.
Migration strategy
Incremental migration
Both packages can coexist. Migrate file by file:
{
"dependencies": {
"@solana/web3.js": "^1.95.0",
"@solana/kit": "^2.0.0"
}
}
Convert one module at a time. Helper functions to convert between types:
import { PublicKey } from "@solana/web3.js";
import { address } from "@solana/kit";
// web3.js PublicKey → kit address
const addr = address(publicKey.toBase58());
// kit address → web3.js PublicKey
const pubkey = new PublicKey(addr);
Recommended migration order
- RPC calls (read operations) — lowest risk, easiest to verify.
- Address handling — replace
PublicKeywithaddress(). - Transaction building — most complex, test thoroughly.
- Signing — update to signer abstractions.
- Remove web3.js 1.x — once all code is migrated.
Common patterns
Error handling
// @solana/kit
try {
await signAndSendTransactionMessageWithSigners(tx);
} catch (e) {
if (isSolanaError(e, SOLANA_ERROR__TRANSACTION_ERROR__BLOCKHASH_NOT_FOUND)) {
// Handle specific error
}
}
Errors are typed constants, not string matching.
Subscriptions
// @solana/kit
const rpcSubscriptions = createSolanaRpcSubscriptions(wsUrl);
const notifications = await rpcSubscriptions
.accountNotifications(addr, { commitment: "confirmed" })
.subscribe({ abortSignal: controller.signal });
for await (const notification of notifications) {
// Handle account update
}
Subscriptions use async iterables with abort signal support.
FAQ
Is @solana/web3.js 1.x deprecated? It is in maintenance mode. Critical bugs are fixed, but new features go to @solana/kit. Plan to migrate for long-term support.
Does @solana/kit work with wallet adapters?
Yes. The @solana/wallet-adapter package supports both web3.js 1.x and @solana/kit signers.
Can I use @solana/kit in a browser? Yes. The tree-shakeable design is optimized for browser bundles. Only import what you use.
Do I need to update my Anchor code? Anchor generates its own TypeScript client code. Check Anchor’s documentation for @solana/kit compatibility in your Anchor version.
Updated: