Use when adding new error messages to React, or seeing "unknown error code" warnings.
npx skills add getpara/ai-tooling --skill "para-migration"
Install specific skill from multi-skill repository
# Description
Migrate from other wallet providers (Privy, Dynamic, Reown, etc.) to Para
# SKILL.md
name: para-migration
description: Migrate from other wallet providers (Privy, Dynamic, Reown, etc.) to Para
Para Migration Skill
Migrate from other embedded wallet or auth providers to Para. This skill covers provider detection, step-by-step migration guides, hook/component mapping tables, and common post-migration issues.
Provider Detection
Identify what the project currently uses by checking package.json dependencies:
| Dependency Pattern | Current Provider |
|---|---|
@privy-io/react-auth, @privy-io/expo |
Privy |
@dynamic-labs/sdk-react-core, @dynamic-labs/ethereum |
Dynamic |
@web3modal/wagmi, @reown/appkit |
Reown (formerly WalletConnect Web3Modal) |
@web3auth/modal, @web3auth/no-modal |
Web3Auth |
magic-sdk, @magic-sdk/react-native |
Magic |
thirdweb, @thirdweb-dev/react |
Thirdweb |
@turnkey/sdk-react, @turnkey/http |
Turnkey |
@usecapsule/* |
Capsule (legacy Para branding) |
Additional Detection Signals
- Privy: Look for
<PrivyProvider>,usePrivy(),useWallets()in source - Dynamic: Look for
<DynamicContextProvider>,useDynamicContext()in source - Reown: Look for
createWeb3Modal(),<Web3Modal>,useWeb3Modal()in source - Web3Auth: Look for
<Web3AuthProvider>,web3auth.connect()in source - Magic: Look for
new Magic(),magic.authin source
Migration from Privy
Step 1: Replace Packages
Remove:
npm uninstall @privy-io/react-auth @privy-io/wagmi @privy-io/expo
Install:
npm install @getpara/react-sdk @tanstack/react-query
Step 2: Replace Provider
Before (Privy):
import { PrivyProvider } from "@privy-io/react-auth";
<PrivyProvider
appId="your-privy-app-id"
config={{
loginMethods: ["email", "google", "apple"],
appearance: { theme: "light" },
}}>
{children}
</PrivyProvider>
After (Para):
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Environment, ParaProvider as ParaSDKProvider } from "@getpara/react-sdk";
import "@getpara/react-sdk/styles.css";
const queryClient = new QueryClient();
<QueryClientProvider client={queryClient}>
<ParaSDKProvider
paraClientConfig={{
apiKey: process.env.NEXT_PUBLIC_PARA_API_KEY!,
env: Environment.BETA,
}}
config={{ appName: "My App" }}
paraModalConfig={{
disableEmailLogin: false,
oAuthMethods: ["GOOGLE", "APPLE"],
authLayout: ["AUTH:FULL", "EXTERNAL:FULL"],
}}>
{children}
</ParaSDKProvider>
</QueryClientProvider>
Step 3: Replace Hooks
| Privy | Para | Notes |
|---|---|---|
usePrivy() |
useAccount() from @getpara/react-sdk |
isConnected, address |
usePrivy().login() |
useModal().openModal() |
Opens Para auth modal |
usePrivy().logout() |
useLogout().logout() |
|
usePrivy().authenticated |
useAccount().isConnected |
|
usePrivy().user |
useAccount() + useLinkedAccounts() |
|
useWallets() |
useAccount().embedded.wallets |
Wallet array |
usePrivy().sendTransaction() |
Use viem/ethers signer | See signing integration |
Migration from Dynamic
Step 1: Replace Packages
Remove:
npm uninstall @dynamic-labs/sdk-react-core @dynamic-labs/ethereum @dynamic-labs/wagmi-connector
Install:
npm install @getpara/react-sdk @tanstack/react-query
Step 2: Replace Provider
Before (Dynamic):
import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";
<DynamicContextProvider
settings={{
environmentId: "your-dynamic-env-id",
walletConnectors: [EthereumWalletConnectors],
}}>
{children}
</DynamicContextProvider>
After (Para):
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Environment, ParaProvider as ParaSDKProvider } from "@getpara/react-sdk";
import "@getpara/react-sdk/styles.css";
const queryClient = new QueryClient();
<QueryClientProvider client={queryClient}>
<ParaSDKProvider
paraClientConfig={{
apiKey: process.env.NEXT_PUBLIC_PARA_API_KEY!,
env: Environment.BETA,
}}
config={{ appName: "My App" }}
paraModalConfig={{
authLayout: ["AUTH:FULL", "EXTERNAL:FULL"],
oAuthMethods: ["GOOGLE", "APPLE"],
}}>
{children}
</ParaSDKProvider>
</QueryClientProvider>
Step 3: Replace Hooks
| Dynamic | Para | Notes |
|---|---|---|
useDynamicContext() |
useAccount() |
|
useDynamicContext().setShowAuthFlow(true) |
useModal().openModal() |
|
useDynamicContext().handleLogOut() |
useLogout().logout() |
|
useDynamicContext().primaryWallet |
useWallet() |
Active wallet |
useDynamicContext().isAuthenticated |
useAccount().isConnected |
Migration from Reown (WalletConnect Web3Modal)
Step 1: Replace Packages
Remove:
npm uninstall @web3modal/wagmi @web3modal/ethereum @reown/appkit @reown/appkit-wagmi
Install (option A -- full Para modal):
npm install @getpara/react-sdk @tanstack/react-query
Install (option B -- keep wagmi, add Para as connector):
npm install @getpara/react-sdk-lite @getpara/wagmi-v2-integration wagmi viem @tanstack/react-query
Step 2: Replace Config (Option B -- Wagmi Connector)
Before (Reown):
import { createWeb3Modal } from "@web3modal/wagmi/react";
import { createConfig, http } from "wagmi";
import { sepolia } from "wagmi/chains";
const config = createConfig({
chains: [sepolia],
transports: { [sepolia.id]: http() },
});
createWeb3Modal({ wagmiConfig: config, projectId: "your-wc-project-id" });
After (Para wagmi connector):
import { paraConnector } from "@getpara/wagmi-v2-integration";
import { ParaWeb, Environment } from "@getpara/react-sdk-lite";
import { createConfig, http } from "wagmi";
import { sepolia } from "wagmi/chains";
const para = new ParaWeb(Environment.BETA, process.env.NEXT_PUBLIC_PARA_API_KEY!);
const connector = paraConnector({
para,
appName: "My App",
chains: [sepolia],
oAuthMethods: ["GOOGLE", "APPLE"],
});
export const config = createConfig({
chains: [sepolia],
connectors: [connector],
transports: { [sepolia.id]: http() },
});
Step 3: Replace Hooks
| Reown | Para (wagmi connector) | Notes |
|---|---|---|
useWeb3Modal().open() |
useConnect() with para connector |
Standard wagmi hook |
useDisconnect() |
useDisconnect() |
Same wagmi hook |
useAccount() |
useAccount() |
Same wagmi hook |
Migration from Web3Auth
Step 1: Replace Packages
Remove:
npm uninstall @web3auth/modal @web3auth/no-modal @web3auth/ethereum-provider @web3auth/base
Install:
npm install @getpara/react-sdk @tanstack/react-query
Step 2: Replace Setup
Before (Web3Auth):
import { Web3Auth } from "@web3auth/modal";
const web3auth = new Web3Auth({
clientId: "your-web3auth-client-id",
chainConfig: { chainNamespace: "eip155", chainId: "0xaa36a7" },
});
await web3auth.initModal();
const provider = await web3auth.connect();
After (Para):
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Environment, ParaProvider as ParaSDKProvider } from "@getpara/react-sdk";
import "@getpara/react-sdk/styles.css";
const queryClient = new QueryClient();
<QueryClientProvider client={queryClient}>
<ParaSDKProvider
paraClientConfig={{
apiKey: process.env.NEXT_PUBLIC_PARA_API_KEY!,
env: Environment.BETA,
}}
config={{ appName: "My App" }}
paraModalConfig={{
oAuthMethods: ["GOOGLE", "APPLE", "DISCORD"],
authLayout: ["AUTH:FULL", "EXTERNAL:FULL"],
}}>
{children}
</ParaSDKProvider>
</QueryClientProvider>
Step 3: Replace Hooks
| Web3Auth | Para | Notes |
|---|---|---|
web3auth.connect() |
useModal().openModal() |
|
web3auth.logout() |
useLogout().logout() |
|
web3auth.connected |
useAccount().isConnected |
|
web3auth.provider |
useClient() + signer integration |
Use viem/ethers |
Migration from Magic
Step 1: Replace Packages
Remove:
npm uninstall magic-sdk @magic-sdk/react-native @magic-ext/oauth
Install:
npm install @getpara/react-sdk @tanstack/react-query
Step 2: Replace Setup
Before (Magic):
import { Magic } from "magic-sdk";
import { OAuthExtension } from "@magic-ext/oauth";
const magic = new Magic("your-magic-publishable-key", {
extensions: [new OAuthExtension()],
});
After (Para):
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Environment, ParaProvider as ParaSDKProvider } from "@getpara/react-sdk";
import "@getpara/react-sdk/styles.css";
const queryClient = new QueryClient();
<QueryClientProvider client={queryClient}>
<ParaSDKProvider
paraClientConfig={{
apiKey: process.env.NEXT_PUBLIC_PARA_API_KEY!,
env: Environment.BETA,
}}
config={{ appName: "My App" }}
paraModalConfig={{
disableEmailLogin: false,
oAuthMethods: ["GOOGLE", "APPLE"],
}}>
{children}
</ParaSDKProvider>
</QueryClientProvider>
Step 3: Replace Calls
| Magic | Para | Notes |
|---|---|---|
magic.auth.loginWithEmailOTP() |
useModal().openModal() or useSignUpOrLogIn() |
|
magic.oauth.loginWithRedirect() |
useVerifyOAuth() |
|
magic.user.logout() |
useLogout().logout() |
|
magic.user.isLoggedIn() |
useAccount().isConnected |
|
magic.rpcProvider |
useClient() + signer integration |
Use viem/ethers |
Migration from Capsule (Legacy Branding)
If migrating from @usecapsule/* to @getpara/*, see the dedicated guide. The API surface is identical -- only the package names changed:
| Old Package | New Package |
|---|---|
@usecapsule/react-sdk |
@getpara/react-sdk |
@usecapsule/web-sdk |
@getpara/web-sdk |
@usecapsule/server-sdk |
@getpara/server-sdk |
@usecapsule/viem-v2-integration |
@getpara/viem-v2-integration |
@usecapsule/ethers-v6-integration |
@getpara/ethers-v6-integration |
Find-and-replace @usecapsule/ with @getpara/ in all imports and package.json.
Common Post-Migration Issues
Users lose their wallets
Para creates new wallets for users. If your previous provider had user wallets, those wallets remain with the old provider. Para does not import private keys from other providers -- MPC key shares are generated fresh. Communicate this to users and handle any asset migration separately.
OAuth redirect URLs change
Para OAuth uses its own redirect flow. Update your OAuth provider settings (Google Console, Apple Developer, etc.) to include Para's redirect URLs, or use Para's built-in OAuth which handles this automatically.
Session management differences
Para sessions are managed via passkeys and the Para portal. If you previously managed sessions with JWTs from another provider, switch to Para's session model:
const isActive = await para.isSessionActive();
const { token } = await para.issueJWT(); // For your own backend verification
Missing "use client" directive
If migrating a Next.js app, ensure the ParaProvider wrapper file has "use client" at the top. Previous providers may not have required this.
Missing CSS import
Para's modal requires its stylesheet. Add to your root layout:
import "@getpara/react-sdk/styles.css";
@tanstack/react-query not installed
Para's React SDK depends on React Query. If your previous provider did not use it:
npm install @tanstack/react-query
Wrap your app with QueryClientProvider (see setup examples above).
Environment variable prefix changes
Update environment variable names to match Para's conventions:
| Previous Variable | Para Variable |
|---|---|
PRIVY_APP_ID |
NEXT_PUBLIC_PARA_API_KEY |
DYNAMIC_ENVIRONMENT_ID |
NEXT_PUBLIC_PARA_API_KEY |
WEB3AUTH_CLIENT_ID |
NEXT_PUBLIC_PARA_API_KEY |
MAGIC_PUBLISHABLE_KEY |
NEXT_PUBLIC_PARA_API_KEY |
Signing library compatibility
Para works with the same signing libraries (viem, ethers, cosmjs, solana web3.js). You likely do not need to change your signing code beyond swapping the signer constructor:
// Before (generic provider)
const signer = new ethers.BrowserProvider(window.ethereum).getSigner();
// After (Para)
import { ParaEthersSigner } from "@getpara/ethers-v6-integration";
const signer = new ParaEthersSigner(para, provider);
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.