miniapp-to-web
Migrate Mini App to Standalone Web App
You are converting a World App mini app to also work in a regular browser. MiniKit commands auto-detect the environment — inside World App they use the native bridge, outside they fall back to Wagmi. Follow these steps in order.
Step 1 — Install dependencies
pnpm add wagmi @tanstack/react-query siwe
wagmi— wallet connection and transaction execution on web@tanstack/react-query— peer dependency of wagmisiwe— needed forwalletAuthSIWE message construction on web
Step 2 — Create Wagmi config
// config.ts
import { worldApp } from '@worldcoin/minikit-js/wagmi';
import { createConfig, http } from 'wagmi';
import { worldchain } from 'wagmi/chains';
import { injected } from 'wagmi/connectors';
export const config = createConfig({
chains: [worldchain],
connectors: [worldApp(), injected()],
transports: {
[worldchain.id]: http(),
},
});
worldApp() handles World App, injected() handles browser wallets (MetaMask, etc).
Step 3 — Update providers
Wrap with WagmiProvider and QueryClientProvider, and pass wagmiConfig to MiniKitProvider:
'use client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { MiniKitProvider } from '@worldcoin/minikit-js/minikit-provider';
import { WagmiProvider } from 'wagmi';
import { config } from './config';
const queryClient = new QueryClient();
export default function Providers({ children }: { children: React.ReactNode }) {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<MiniKitProvider
props={{
appId: process.env.NEXT_PUBLIC_APP_ID!,
wagmiConfig: config,
}}
>
{children}
</MiniKitProvider>
</QueryClientProvider>
</WagmiProvider>
);
}
MiniKitProvider calls setWagmiConfig(config) internally which registers the Wagmi fallback adapter. WagmiProvider and QueryClientProvider are needed for Wagmi hooks like useWaitForTransactionReceipt.
Step 4 — Auth works automatically
MiniKit.walletAuth() requires no code changes:
- World App: native SIWE flow via Smart Account (Safe)
- Web: connects via Wagmi connector, signs SIWE message
const result = await MiniKit.walletAuth({
nonce,
statement: 'Sign in to my app',
});
// result.executedWith === "minikit" | "wagmi"
Backend verification handles both account types
verifySiweMessage from @worldcoin/minikit-js/siwe auto-detects:
- Smart Account (Safe): EIP-1271
isValidSignatureon-chain - EOA: ECDSA
recoverMessageAddress
import { verifySiweMessage } from '@worldcoin/minikit-js/siwe';
const { isValid, siweMessageData } = await verifySiweMessage(
payload, // { message, signature, address }
nonce,
);
No branching needed — it checks if the address has contract code and picks the right method.
Step 5 — Transactions work automatically
MiniKit.sendTransaction() requires no code changes. World Chain only (chainId 480).
- World App: native bridge, supports batching, returns
userOpHash - Web (single tx): sent directly via Wagmi wallet
- Web (multiple txs): executed sequentially — each requires wallet confirmation (not atomic)
import { MiniKit } from '@worldcoin/minikit-js';
import { encodeFunctionData } from 'viem';
const result = await MiniKit.sendTransaction({
chainId: 480,
transactions: [
{
to: PERMIT2,
data: encodeFunctionData({
abi: permit2Abi,
functionName: 'approve',
args: [TOKEN, SPENDER, amount, 0],
}),
},
{
to: CONTRACT,
data: encodeFunctionData({
abi: contractAbi,
functionName: 'swap',
args: [amount],
}),
},
],
});
Handle transaction receipts by environment
import { useUserOperationReceipt } from '@worldcoin/minikit-react';
const { poll } = useUserOperationReceipt({ client });
if (result.executedWith === 'minikit') {
// World App: poll for UserOperation receipt
await poll(result.data.userOpHash);
} else {
// Web: standard tx hash
await publicClient.waitForTransactionReceipt({
hash: result.data.userOpHash as `0x${string}`,
});
}
Step 6 — World ID needs no changes
World ID uses IDKit, which is independent of the wallet layer. It works the same everywhere.
Step 7 — Other commands need fallbacks
Commands without built-in Wagmi adapters (pay, shareContacts, sendHapticFeedback, etc.) need a fallback function for web:
const result = await MiniKit.pay({
amount: '1.00',
token: 'USDCE',
reference: 'order-123',
description: 'Coffee',
fallback: async () => {
// Custom web payment flow
return myCustomPaymentResult;
},
});
Without a fallback, these throw CommandUnavailableError on web.
What has built-in Wagmi support
| Command | Wagmi fallback | Notes |
|---|---|---|
walletAuth |
Yes | SIWE via Wagmi connector |
sendTransaction |
Yes | Single or sequential on web |
signMessage |
Yes | personal_sign via Wagmi |
signTypedData |
Yes | EIP-712 via Wagmi |
pay |
No | Provide fallback |
shareContacts |
No | Provide fallback |
verify (World ID) |
N/A | Uses IDKit directly |
| Everything else | No | Provide fallback |
Key differences between environments
| Aspect | World App | Web |
|---|---|---|
| Account type | Smart Account (Safe) | EOA |
| Transaction hash | userOpHash (UserOperation) |
Standard tx hash |
| Multi-tx | Native atomic batching | Sequential (not atomic) |
| Gas | Sponsored | User pays |
| Chain | World Chain (480) | World Chain (480) |