in-solana by Supanode

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

Featureweb3.js 1.x@solana/kit
Bundle size~300 KBTree-shakeable, often < 50 KB
ArchitectureMonolithic class-basedModular, functional
TypeScriptBasic typesBranded types, full type safety
CodecsBuilt-in, limitedComposable, extensible
RPC typesUntyped responsesFully typed RPC responses
MaintenanceLegacy, limited updatesActively 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);
  1. RPC calls (read operations) — lowest risk, easiest to verify.
  2. Address handling — replace PublicKey with address().
  3. Transaction building — most complex, test thoroughly.
  4. Signing — update to signer abstractions.
  5. 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: