Skip to main content
Version: 0.96.0

Browser Setup

The CCIP SDK works in browser environments with proper bundler configuration. This guide covers polyfill requirements and common bundler setups.

Polyfill Requirements

SDK-Only Projects

If you're using @chainlink/ccip-sdk without wallet UI libraries, you only need the buffer polyfill:

PolyfillRequired ByReason
bufferCCIP SDK (Solana, TON, Sui chains)Binary data handling

With Wallet Libraries

If you're using wallet connection libraries (RainbowKit, MetaMask SDK, Solana Wallet Adapter), additional polyfills are needed:

PolyfillRequired ByNOT Required By
buffer@chainlink/ccip-sdk-
crypto@metamask/sdk, wallet libraries@chainlink/ccip-sdk
stream@metamask/sdk@chainlink/ccip-sdk
process@metamask/sdk, @walletconnect@chainlink/ccip-sdk
util@solana/wallet-adapter@chainlink/ccip-sdk

Vite Configuration

TypeScript
// vite.config.ts - Minimal config for CCIP SDK only
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { nodePolyfills } from 'vite-plugin-node-polyfills'

export default defineConfig({
plugins: [
react(),
nodePolyfills({
include: ['buffer'],
globals: { Buffer: true },
}),
],
})

Install the polyfill plugin:

Bash
npm install vite-plugin-node-polyfills

Webpack Configuration

JavaScript
// webpack.config.js - Minimal config
const webpack = require('webpack')

module.exports = {
resolve: {
fallback: {
buffer: require.resolve('buffer/'),
},
},
plugins: [
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
}),
],
}

Install polyfill packages:

Bash
npm install buffer
# If using wallet libraries:
npm install crypto-browserify stream-browserify util process

Next.js Configuration

JavaScript
// next.config.js
const nextConfig = {
webpack: (config) => {
config.resolve.fallback = {
...config.resolve.fallback,
buffer: require.resolve('buffer/'),
// Add more if using wallet libraries
}
return config
},
}

module.exports = nextConfig

Tree-Shaking

The SDK supports tree-shaking - only the chains you import are bundled:

TypeScript
// Only EVMChain is bundled
import { EVMChain } from '@chainlink/ccip-sdk'

// Only SolanaChain is bundled
import { SolanaChain } from '@chainlink/ccip-sdk'

// All chains - largest bundle, use only if needed
import { allSupportedChains } from '@chainlink/ccip-sdk/all'

Bundle Sizes

ImportMinifiedGzipped
EVM only740 KB~180 KB
Solana only1.2 MB~290 KB
Aptos only1.4 MB~340 KB
TON only1.0 MB~240 KB
EVM + Solana1.4 MB~340 KB
All chains3.0 MB~720 KB

Important: Don't place @chainlink/ccip-sdk in manualChunks configuration, as this disables tree-shaking:

TypeScript
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
'vendor-react': ['react', 'react-dom'],
'vendor-solana': ['@solana/web3.js'],
// DON'T add @chainlink/ccip-sdk here
},
},
},
},
})

Fetch Binding Fix

Some bundlers break the native fetch context. If you see "Illegal invocation" errors:

TypeScript
// main.tsx or index.tsx - Add at the top
if (typeof globalThis.fetch === 'function') {
const originalFetch = globalThis.fetch
globalThis.fetch = (input, init) => originalFetch.call(globalThis, input, init)
}

Using with Wagmi/Viem

When using the SDK with wagmi, you may encounter type mismatches with fromViemClient():

TypeScript
import { getPublicClient } from '@wagmi/core'
import { fromViemClient } from '@chainlink/ccip-sdk/viem'
import type { PublicClient, Transport, Chain } from 'viem'

// Type bridge for wagmi compatibility
function toGenericPublicClient(
client: ReturnType<typeof getPublicClient>
): PublicClient<Transport, Chain> {
return client as PublicClient<Transport, Chain>
}

// Usage
const wagmiClient = getPublicClient(wagmiConfig, { chainId: 11155111 })
const publicClient = toGenericPublicClient(wagmiClient)
const chain = await fromViemClient(publicClient)

This cast is safe when both packages use the same viem version.

Complete Vite Example

Full configuration for a React app with EVM and Solana support:

TypeScript
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { nodePolyfills } from 'vite-plugin-node-polyfills'

export default defineConfig({
plugins: [
react(),
nodePolyfills({
include: ['buffer', 'crypto', 'stream', 'util', 'process'],
globals: {
Buffer: true,
global: true,
process: true,
},
}),
],
define: {
'process.env': {},
},
build: {
rollupOptions: {
output: {
manualChunks: {
'vendor-react': ['react', 'react-dom'],
'vendor-solana': ['@solana/web3.js', '@solana/wallet-adapter-base'],
'vendor-evm': ['wagmi', 'viem', '@rainbow-me/rainbowkit'],
// Don't include @chainlink/ccip-sdk - let it tree-shake
},
},
},
},
optimizeDeps: {
include: ['buffer'],
},
})

Framework Integration

Next.js

Bash
npm install buffer
JavaScript
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.fallback = {
...config.resolve.fallback,
buffer: require.resolve('buffer/'),
}
}
return config
},
}
module.exports = nextConfig

For client components using Solana/TON, add at the top:

TSX
'use client'
import { Buffer } from 'buffer'
if (typeof window !== 'undefined') {
window.Buffer = Buffer
}

Remix

Remix uses esbuild under the hood. Add the buffer shim to your client entry:

Bash
npm install buffer
TypeScript
// app/entry.client.tsx
import { Buffer } from 'buffer'
globalThis.Buffer = Buffer

// ... rest of entry.client.tsx

Verify Your Setup

After configuring your bundler, verify the polyfill is working:

TypeScript
// Add to your app's entry point or browser console
console.log('Buffer available:', typeof Buffer !== 'undefined')
console.log('Buffer works:', Buffer.from('test').toString('hex') === '74657374')

Check bundle size to verify tree-shaking:

Bash
# Check output file size
ls -lh dist/*.js

# For detailed analysis
npx source-map-explorer dist/bundle.js

Expected sizes for EVM-only: ~740 KB minified, ~180 KB gzipped.

Troubleshooting

"Buffer is not defined"

Cause: Using Solana or TON chains without the Buffer polyfill, or the polyfill isn't loading before SDK code.

Solution:

  1. Verify the polyfill configuration for your bundler
  2. Ensure buffer package is installed: npm ls buffer
  3. For Bun, use a custom build script to ensure correct load order

"process is not defined"

Cause: Wallet libraries (MetaMask SDK, WalletConnect) require process.

Solution: Add the process polyfill if using wallet libraries that require it.

"Illegal invocation" on fetch

Cause: Some bundlers break the native fetch context.

Solution: Add the fetch binding fix shown above.

Bundle size larger than expected

Cause: Tree-shaking may not be working, or you're importing more chains than needed.

Symptoms: EVM-only bundle exceeds 1 MB.

Solution:

  1. Verify you're using ES module imports (not require())
  2. Check you're importing specific chains, not allSupportedChains
  3. Run a bundle analyzer:
    Bash
    # Webpack
    npx webpack-bundle-analyzer dist/stats.json

    # Vite
    npx vite-bundle-visualizer

Vite dev server errors with EVM-only code

Cause: Vite pre-bundles all SDK dependencies in development mode, including Solana/TON libraries that need Buffer.

Solution: Add the Buffer polyfill even for EVM-only development. This is only needed for vite dev; production builds will tree-shake correctly.

Type errors with wagmi

Cause: Wagmi's getPublicClient() returns a chain-specific typed client that doesn't match the SDK's expected type.

Solution: Use the type bridge function shown in "Using with Wagmi/Viem" section, or see the Viem Integration guide.

"Cannot find module 'buffer'"

Cause: The buffer package is not installed.

Solution:

Bash
npm install buffer