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
The buffer polyfill is required by the SDK's Solana, TON, and Sui chain implementations. EVM-only and Aptos-only consumers do not need buffer at runtime.
| Polyfill | Required By | NOT Required By |
|---|---|---|
buffer | Solana, TON, Sui chains | EVM, Aptos |
Bundler behavior differs:
- Vite: Always include the
bufferpolyfill, even for EVM-only projects. Vite's dev server pre-bundles all SDK dependencies eagerly (including Solana/TON code), so the polyfill must be resolvable. - Webpack/Next.js: EVM-only consumers do not need the
bufferpolyfill. Non-EVM code is tree-shaken in production and never executed at runtime in development.
With Wallet Libraries
If you're using wallet connection libraries (RainbowKit, MetaMask SDK, Solana Wallet Adapter), additional polyfills are needed:
| Polyfill | Required By | NOT Required By |
|---|---|---|
buffer | Solana, TON, Sui chains | EVM, Aptos |
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 |
Dependency Version Conflict (@noble/hashes)
The SDK depends on ethers (which pins @noble/hashes@1.3.2) and @mysten/sui (which requires @noble/hashes@^1.8.0). Node.js handles this automatically via nested node_modules, but Webpack and Next.js may resolve to the wrong version, causing ERR_PACKAGE_PATH_NOT_EXPORTED.
Add version overrides to your project's package.json to force a single compatible version:
- npm
- yarn
- pnpm
{
"overrides": {
"@noble/hashes": "^1.8.0"
}
}
{
"resolutions": {
"**/@noble/hashes": "^1.8.0"
}
}
{
"pnpm": {
"overrides": {
"@noble/hashes": "^1.8.0"
}
}
}
This is safe — @noble/hashes@1.8.0 is backward-compatible with ethers' needs.
In addition, add a resolve.alias in your bundler config to ensure the correct version is used (see the Webpack and Next.js sections below).
Vite Configuration
- SDK Only
- With Wallet Libraries
// 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 },
}),
],
})
// vite.config.ts - Full config with wallet libraries
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': {},
},
})
Install the polyfill plugin:
npm install vite-plugin-node-polyfills
Webpack Configuration
- EVM Only
- Multi-Chain
- With Wallet Libraries
// webpack.config.js - EVM-only (no polyfills needed)
const path = require('path')
module.exports = {
resolve: {
alias: {
'@noble/hashes': path.dirname(require.resolve('@noble/hashes')),
},
},
}
EVM-only consumers do not need the buffer polyfill on Webpack. Non-EVM code is tree-shaken in production and never executed in development.
// webpack.config.js - Multi-chain (Solana, TON, Sui)
const path = require('path')
const webpack = require('webpack')
module.exports = {
resolve: {
alias: {
'@noble/hashes': path.dirname(require.resolve('@noble/hashes')),
},
fallback: {
buffer: require.resolve('buffer/'),
},
},
plugins: [
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
}),
],
}
// webpack.config.js - Full config with wallet libraries
const path = require('path')
const webpack = require('webpack')
module.exports = {
resolve: {
alias: {
'@noble/hashes': path.dirname(require.resolve('@noble/hashes')),
},
fallback: {
buffer: require.resolve('buffer/'),
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
util: require.resolve('util/'),
process: require.resolve('process/browser'),
},
},
plugins: [
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
process: 'process/browser',
}),
],
}
Install polyfill packages:
# If using Solana, TON, or Sui chains:
npm install buffer
# If using wallet libraries:
npm install crypto-browserify stream-browserify util process
Next.js Configuration
Recommended: Next.js >= 13.4.5 (supports moduleResolution: "bundler"). For older versions, see the legacy workaround below.
- EVM Only
- Multi-Chain
// next.config.js
const path = require('path')
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: (config, { isServer }) => {
if (!isServer) {
// Fix @noble/hashes version conflict
config.resolve.alias = {
...config.resolve.alias,
'@noble/hashes': path.dirname(require.resolve('@noble/hashes')),
}
}
return config
},
}
module.exports = nextConfig
EVM-only consumers do not need the buffer polyfill on Next.js.
// next.config.js
const path = require('path')
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: (config, { isServer }) => {
if (!isServer) {
// Fix @noble/hashes version conflict
config.resolve.alias = {
...config.resolve.alias,
'@noble/hashes': path.dirname(require.resolve('@noble/hashes')),
}
config.resolve.fallback = {
...config.resolve.fallback,
buffer: require.resolve('buffer/'),
}
}
return config
},
}
module.exports = nextConfig
Tree-Shaking
The SDK supports tree-shaking — only the chains you import are bundled:
// 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'
Webpack/Next.js: Tree-shaking requires the @noble/hashes version fix first. Without it, Webpack fails at module resolution before tree-shaking can run.
Bundle Sizes
| Import | Minified | Gzipped |
|---|---|---|
| EVM only | 740 KB | ~180 KB |
| Solana only | 1.2 MB | ~290 KB |
| Aptos only | 700 KB | ~170 KB |
| Sui only | 756 KB | ~185 KB |
| TON only | 760 KB | ~185 KB |
| EVM + Solana | 1.4 MB | ~340 KB |
| All chains | 2.0 MB | ~480 KB |
Important: Don't place @chainlink/ccip-sdk in manualChunks configuration, as this disables tree-shaking:
// 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
},
},
},
},
})
Using with Wagmi/Viem
When using the SDK with wagmi, you may encounter type mismatches with fromViemClient():
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:
// 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
See the Next.js Configuration section above for webpack config (including the required @noble/hashes resolve.alias fix).
For client components using Solana/TON/Sui, install the buffer polyfill and add at the top:
npm install buffer
'use client'
import { Buffer } from 'buffer'
if (typeof window !== 'undefined') {
window.Buffer = Buffer
}
Next.js 12 – 13.4.4 (Legacy)
These versions force moduleResolution: "node", which cannot resolve the SDK's type declarations. Add the following paths mapping to your tsconfig.json:
{
"compilerOptions": {
"paths": {
"@chainlink/ccip-sdk": [
"./node_modules/@chainlink/ccip-sdk/src/index.ts"
],
"@chainlink/ccip-sdk/viem": [
"./node_modules/@chainlink/ccip-sdk/src/viem.ts"
]
}
}
}
Remix
Remix uses esbuild under the hood. Add the buffer shim to your client entry:
npm install buffer
// 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:
// 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:
# 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
"ERR_PACKAGE_PATH_NOT_EXPORTED"
Cause: ethers pins @noble/hashes@1.3.2 while other SDK dependencies require @noble/hashes@1.8.0+. Webpack may resolve to the v1.3.2 copy, which lacks sub-paths like ./blake2.js and ./sha2.js that were added in v1.8.0.
Solution: Add version overrides to your package.json and resolve.alias to your bundler config. See the Dependency Version Conflict section above.
"Buffer is not defined"
Cause: Using Solana or TON chains without the Buffer polyfill, or the polyfill isn't loading before SDK code.
Solution:
- Verify the polyfill configuration for your bundler
- Ensure
bufferpackage is installed:npm ls buffer - 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.
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:
- Verify you're using ES module imports (not
require()) - Check you're importing specific chains, not
allSupportedChains - 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:
npm install buffer
Related
- Viem Integration - Use viem clients with the SDK
- Multi-Chain Support - Work with different blockchain families