Plugins

Understand Kit's plugin ecosystem

Plugins are how Kit clients gain capabilities. Everything from RPC connectivity, wallet signers, transaction sending, and even typed access to Solana programs is delivered as a plugin you opt into. This page explains the underlying client model and the conventions plugins follow.

What is a Kit client?

A Kit client is a frozen, typed JavaScript object created with createClient() from @solana/kit and extended by chaining .use(plugin()) calls. Each call applies a plugin, and the returned client is a brand new immutable object — the previous one is left untouched.

import {  } from '@solana/kit';
import {  } from '@solana/kit-plugin-rpc';
import {  } from '@solana/kit-plugin-signer';
 
const  = await ().(()).(());

Because the client is immutable, you cannot accidentally mutate it from somewhere else in your application: client always reflects the exact set of plugins you applied at creation time.

What is a plugin?

A plugin is a function that takes a client object and returns either a new client object or a promise resolving to one. The shape is captured by the ClientPlugin<TInput, TOutput> type.

import { ,  } from '@solana/kit';
 
const  = (): <object, { : 'apple' }> => () =>
    (, { : 'apple' as  });

Most plugins follow the same convention: they are exported as small factory functions (so you can pass configuration to them) that return the actual ClientPlugin function. Asynchronous plugins are supported too; the .use(...) chain awaits them automatically, so you only need a single await at the end.

Plugin requirements and ordering

Plugins can require capabilities from the input client by constraining the input type. This means installing them in the wrong order produces a TypeScript error rather than a runtime surprise.

import { , ,  } from '@solana/kit';
 
function () {
    return < extends >(: ) => (, { : true });
}
 
// Requires a `payer` to be installed first; this fails at compile time.
const  = ().(airdropPayer());
Argument of type '<T extends ClientWithPayer>(client: T) => Omit<T, "airdropped"> & { airdropped: boolean; }' is not assignable to parameter of type 'ClientPlugin<object, Omit<ClientWithPayer, "airdropped"> & { airdropped: boolean; }>'. Types of parameters 'client' and 'input' are incompatible. Property 'payer' is missing in type '{}' but required in type 'ClientWithPayer'.

Adding payer(...) (or any other plugin that satisfies ClientWithPayer) before airdropPayer() makes the chain type-check. The TypeScript compiler walks the whole chain and verifies that each plugin's input requirements are met by the time it is applied.

Standard plugin interfaces

Kit ships a small set of standard interfaces in @solana/plugin-interfaces (re-exported from @solana/kit) so plugins from different packages can interoperate. A plugin that installs client.rpc declares it provides ClientWithRpc<...>; a plugin that needs a payer constrains its input with ClientWithPayer. The most common interfaces are:

  • ClientWithPayer — installs client.payer.
  • ClientWithIdentity — installs client.identity.
  • ClientWithRpc<TApi> — installs client.rpc.
  • ClientWithRpcSubscriptions<TApi> — installs client.rpcSubscriptions.
  • ClientWithAirdrop — installs client.airdrop.
  • ClientWithGetMinimumBalance — installs client.getMinimumBalance.
  • ClientWithTransactionPlanning — installs client.planTransaction(s).
  • ClientWithTransactionSending — installs client.sendTransaction(s).

Sticking to these interfaces lets plugins from any source compose freely with each other — and lets your application code take a ClientWithRpc rather than caring which specific plugin produced the RPC capability.

Next steps

On this page