web-to-miniapp
Migrate Next.js apps to World App mini apps. Includes setup for MiniKit and debugging SSR hydration issues.
Install
mkdir -p .claude/skills/web-to-miniapp && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/19512" && unzip -o skill.zip -d .claude/skills/web-to-miniapp && rm skill.zipInstalls to .claude/skills/web-to-miniapp
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Use this skill when you are asked to adapt an existing web app to work as a World App mini app, or to share code between a web app and a mini app. This skill covers the technical steps of migration, common issues, and debugging tips. There will be some changes required to contracts and frontend code, but the overall architecture and user experience can remain largely unchanged.Key capabilities
- →Integrate MiniKit into Next.js applications
- →Configure dual-provider wallet connections
- →Bundle token approvals and contract calls atomically
- →Poll for user operation receipts
- →Whitelist contracts and tokens in the developer portal
How it works
The migration involves disabling SSR for MiniKit-dependent components, providing a MiniKitProvider, and using the MiniKit provider for EIP-1193 wallet operations. It replaces standard transaction hashes with userOpHash tracking.
Inputs & outputs
When to use web-to-miniapp
- →Convert a web app to a mini app
- →Install and configure MiniKit
- →Debug SSR hydration mismatches
- →Implement dual-provider wallet connections
About this skill
Migrate Next.js Web App to World App Mini App
You are converting an existing Next.js web app that uses viem to work as a World App mini app. Follow these steps in order.
Step 1 — Install MiniKit
npm install @worldcoin/minikit-js @worldcoin/minikit-react
Step 2 — Disable SSR for pages that use MiniKit
MiniKit depends on window.WorldApp which doesn't exist on the server. SSR causes hydration mismatches that silently break all React event handlers — buttons render but do nothing.
Wrap the page component in a dynamic import with ssr: false:
// src/app/page.tsx
'use client';
import dynamic from 'next/dynamic';
const App = dynamic(() => import('../components/App'), { ssr: false });
export default function Page() {
return <App />;
}
Move the actual page logic into src/components/App.tsx.
Step 3 — Add MiniKitProvider
Create src/app/providers.tsx:
'use client';
import { MiniKitProvider } from '@worldcoin/minikit-js/minikit-provider';
export default function Providers({ children }: { children: React.ReactNode }) {
return <MiniKitProvider>{children}</MiniKitProvider>;
}
Wrap children in src/app/layout.tsx:
import Providers from './providers';
// ...
<body>
<Providers>{children}</Providers>
</body>;
Step 4 — Dual-provider wallet connection
Detect World App with MiniKit.isInWorldApp() and use getWorldAppProvider() as the EIP-1193 provider. Fall back to window.ethereum for browser wallets.
import { MiniKit } from '@worldcoin/minikit-js';
import { getWorldAppProvider } from '@worldcoin/minikit-js';
import { createWalletClient, custom } from 'viem';
import { worldchain } from 'viem/chains';
const provider = MiniKit.isInWorldApp()
? getWorldAppProvider()
: window.ethereum;
const walletClient = createWalletClient({
chain: worldchain,
transport: custom(provider),
});
getWorldAppProvider() is a standard EIP-1193 provider. Under the hood:
eth_requestAccounts→MiniKit.walletAuth()(SIWE sign-in)eth_sendTransaction→MiniKit.sendTransaction()(returnsuserOpHash)eth_chainId→0x1e0(World Chain 480)
All existing writeContract / readContract calls work unchanged through this provider.
Step 5 — Bundle Approve with Contract Calls
World App resets token approvals to 0 after each transaction. A separate approve() followed by a transferFrom() in the next tx will fail, the approval is already gone. Thus you should bundle the approval and your contract call in a single sendTransaction:
import { MiniKit } from '@worldcoin/minikit-js';
import { encodeFunctionData } from 'viem';
await MiniKit.sendTransaction({
chainId: 480,
transactions: [
{
to: TOKEN,
data: encodeFunctionData({
abi: erc20Abi,
functionName: 'approve',
args: [CONTRACT, amount],
}),
},
{
to: CONTRACT,
data: encodeFunctionData({
abi: contractAbi,
functionName: 'swap',
args: [amount],
}),
},
],
});
In World App, these execute atomically. On web, they execute sequentially — each requires a separate wallet confirmation and is not atomic.
Step 6 — Handle userOpHash receipts
MiniKit returns a userOpHash, not a standard tx hash. Use useUserOperationReceipt from @worldcoin/minikit-react to poll for the receipt:
import { useUserOperationReceipt } from '@worldcoin/minikit-react';
import { createPublicClient, http } from 'viem';
import { worldchain } from 'viem/chains';
const client = createPublicClient({
chain: worldchain,
transport: http(),
});
const { poll, isLoading } = useUserOperationReceipt({ client });
// After sendTransaction:
const result = await MiniKit.sendTransaction({ ... });
await poll(result.data.userOpHash);
Step 7 — Whitelist contracts and tokens
In the Developer Portal > Mini App > Permissions, add:
- Permit2 Tokens — every ERC-20 your app transfers
- Contract Entrypoints — every contract your app calls directly
Transactions touching non-whitelisted contracts are blocked with invalid_contract.
| Issue | Symptom | Fix |
|---|---|---|
| SSR hydration mismatch | Buttons render but clicks do nothing | dynamic(..., { ssr: false }) |
MiniKit.isInstalled() before install() | Always false even in World App | Use useMiniKit() hook or window.WorldApp |
Permit2 uses uint160 amounts | Silent overflow | Cast explicitly |
eth_sendTransaction returns userOpHash | waitForTransactionReceipt times out | Use useUserOperationReceipt from @worldcoin/minikit-react |
| Missing contract whitelist | invalid_contract error | Add to Developer Portal permissions |
Tip: Debugging in the webview
There are no browser devtools in World App's webview. Add eruda for a mobile console:
<!-- In layout.tsx body -->
<script src="https://cdn.jsdelivr.net/npm/eruda"></script>
<script>
eruda.init();
</script>
Bonus: Think about World ID
Think about places where you could use privacy preserving sybil resistance with World ID. The World ID SDK
When not to use it
- →Standard web applications without World App integration
- →Environments requiring server-side wallet interaction
Prerequisites
Limitations
- →MiniKit depends on window.WorldApp which is unavailable on the server
- →Token approvals reset to 0 after each transaction
How it compares
This workflow adapts standard web-based wallet interactions to the specific constraints of the World App mobile webview environment.
Compared to similar skills
web-to-miniapp side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| web-to-miniapp (this skill) | 0 | 3mo | Review | Intermediate |
| clerk-custom-ui | 0 | 3mo | Review | Intermediate |
| nextjs-developer | 328 | 2mo | No flags | Advanced |
| landing-page-guide-v2 | 48 | 8mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
clerk-custom-ui
clerk
Custom authentication flows and component appearance - hooks (useSignIn,
nextjs-developer
zenobi-us
Expert Next.js developer mastering Next.js 14+ with App Router and full-stack features. Specializes in server components, server actions, performance optimization, and production deployment with focus on building fast, SEO-friendly applications.
landing-page-guide-v2
bear2u
Create distinctive, high-converting landing pages that combine proven conversion elements with exceptional design quality. Build beautiful, memorable landing pages using Next.js 14+ and ShadCN UI that avoid generic AI aesthetics while following the 11 essential elements framework.
frontend-developer
sickn33
Build React components, implement responsive layouts, and handle client-side state management. Masters React 19, Next.js 15, and modern frontend architecture. Optimizes performance and ensures accessibility. Use PROACTIVELY when creating UI components or fixing frontend issues.
nextjs-best-practices
davila7
Next.js App Router principles. Server Components, data fetching, routing patterns.
frontend-code-review
langgenius
Trigger when the user requests a review of frontend files (e.g., `.tsx`, `.ts`, `.js`). Support both pending-change reviews and focused file reviews while applying the checklist rules.