From eth-skills
Build-to-production pipeline for Scaffold-ETH 2 dApps. Fork mode, IPFS deployment, Vercel deployment, ENS subdomain setup, custom domains, and production checklist.
How this skill is triggered — by the user, by Claude, or both
Slash command
/eth-skills:frontend-playbookThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill covers the mechanics of getting your Scaffold-ETH 2 dApp from a local dev server to a production URL that real users visit. If you skip steps here, your deployment will break in ways that are obvious to users and embarrassing for you.
This skill covers the mechanics of getting your Scaffold-ETH 2 dApp from a local dev server to a production URL that real users visit. If you skip steps here, your deployment will break in ways that are obvious to users and embarrassing for you.
You ran yarn chain instead of yarn fork. Your local chain has no USDC, no Uniswap, no Chainlink oracles. If your dApp touches ANY existing protocol, you need a fork. Always.
You deployed to IPFS without trailingSlash: true. Every route except / returns a 404. Your users see a blank page on /dashboard, /swap, /profile. This is the single most common IPFS deployment bug.
You did not clean the build directory before deploying. Stale .next and out directories contain old assets. Your deployment serves a mix of old and new code. Pages randomly break.
You set environment variables in .env.local but not in Vercel. Your local build works. Your Vercel build fails silently or serves a broken app with undefined config values.
You assumed IPFS gateways are fast. Public IPFS gateways are slow. Your dApp takes 10-30 seconds to load on first visit. Pin your content and use a dedicated gateway.
Use yarn fork instead of yarn chain whenever your dApp interacts with:
# Fork Ethereum mainnet
yarn fork --network mainnet
# Fork Base
yarn fork --network base
# Fork Optimism
yarn fork --network optimism
# Fork Arbitrum
yarn fork --network arbitrum
This starts Anvil with a fork of the specified chain at the latest block. All contract state from that chain is available locally.
On a forked chain, you can impersonate any account:
# In your deploy script or test, impersonate a whale
cast send --unlocked --from 0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8 \
--to YOUR_ADDRESS \
--value 100ether
You can interact with real Uniswap pools, real Aave markets, real Chainlink feeds — all locally, with zero gas cost and instant confirmation.
Fork state is frozen at the block you forked from. New transactions on the live chain are not reflected. If you need fresh state, restart the fork.
RPC rate limits. Forking makes many RPC calls to the upstream provider. Use an Alchemy or Infura key with reasonable limits. Public RPCs will rate-limit you quickly.
Block timestamps. The forked chain starts at the fork block's timestamp. If your contract has time-dependent logic (vesting, lockups), you may need to advance time:
# Advance time by 1 day
cast rpc evm_increaseTime 86400
cast rpc evm_mine
IPFS deployment gives you censorship resistance. No server can be taken down. The content is addressed by its hash — as long as someone pins it, it is available.
In packages/nextjs/next.config.js:
const nextConfig = {
output: "export",
trailingSlash: true, // CRITICAL — without this, routes return 404 on IPFS
images: {
unoptimized: true, // Required for static export
},
};
Why trailingSlash: true is critical:
Without it, Next.js generates:
/dashboard.html
/swap.html
IPFS serves files by exact path. When a user visits /dashboard, IPFS looks for a file literally named dashboard (no extension). It does not find it. 404.
With trailingSlash: true, Next.js generates:
/dashboard/index.html
/swap/index.html
IPFS resolves /dashboard/ to /dashboard/index.html. It works.
This is the number one IPFS deployment bug. It will waste hours of your time if you forget it.
# Remove ALL build artifacts
rm -rf packages/nextjs/.next packages/nextjs/out
# Build the static export
cd packages/nextjs && yarn build
Always clean before deploying. Stale build artifacts cause:
yarn ipfs
This uses BuidlGuidl's IPFS pinning service. It:
out directory to IPFSYou will get a URL like:
https://ipfs.io/ipfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco
Do not skip this. Open the IPFS URL and verify:
/) loads correctlyIf you are not using BuidlGuidl's service:
# Install IPFS CLI
npm install -g ipfs-car
# Build
rm -rf packages/nextjs/.next packages/nextjs/out
cd packages/nextjs && yarn build
# Upload to web3.storage, Pinata, or nft.storage
# Using Pinata as example:
npx pinata-cli upload packages/nextjs/out
Public gateways (ipfs.io, gateway.pinata.cloud, dweb.link) are slow. For production:
Vercel is the fastest path to production. It handles builds, CDN, SSL, and preview deployments automatically.
| Setting | Value |
|---|---|
| Framework Preset | Next.js |
| Root Directory | packages/nextjs |
| Build Command | yarn build (or leave default) |
| Output Directory | Leave default (.next) |
| Install Command | yarn install |
In the Vercel dashboard, go to Settings > Environment Variables. Add:
NEXT_PUBLIC_ALCHEMY_API_KEY=your_key_here
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=your_project_id
Do NOT add:
DEPLOYER_PRIVATE_KEY — this is for contract deployment, not frontendRemember: NEXT_PUBLIC_ variables are embedded in the client bundle. They are visible to anyone. Only put non-secret configuration here.
Scaffold-ETH 2 is a monorepo. Vercel needs to know which package to build.
Create vercel.json in the monorepo root if needed:
{
"buildCommand": "cd packages/nextjs && yarn build",
"installCommand": "yarn install",
"framework": "nextjs",
"outputDirectory": "packages/nextjs/.next"
}
Or configure via the Vercel dashboard — set Root Directory to packages/nextjs.
# Install Vercel CLI
npm i -g vercel
# Login
vercel login
# Deploy preview
vercel
# Deploy production
vercel --prod
Or just push to main — Vercel auto-deploys on push if you connected the repo.
Monorepo root: If Vercel builds from the wrong directory, all imports fail. Double-check the root directory setting.
Environment variables must be set in Vercel dashboard, not just in .env.local. Your local .env.local is not uploaded to Vercel.
Build errors: If the build fails on Vercel but works locally, check:
Preview deployments: Every PR gets a preview URL. Use this for QA before merging to main.
ENS gives your dApp a human-readable name. Instead of https://my-dapp.vercel.app, users go to mydapp.eth (via ENS-aware browsers) or mydapp.eth.limo (via any browser).
This is the decentralized option. Your frontend lives on IPFS, your name lives on ENS.
Get an ENS name — Register at app.ens.domains
Set the Content Hash:
ipfs://QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco
Access via .eth.limo:
https://yourname.eth.limo.eth.limo is a public gateway that resolves ENS content hashesUpdate on redeploy: Every time you redeploy to IPFS, you get a new CID. You must update the ENS content hash (onchain transaction, costs gas).
If you are using Vercel or another host:
urlhttps://my-dapp.vercel.appThis is simpler but less decentralized — your host can go down.
If you own yourbrand.eth, you can create subdomains like app.yourbrand.eth:
app subdomainCost: One onchain transaction for the subdomain creation + one for setting the content hash.
app.yourdapp.com)76.76.21.21cname.vercel-dns.com (for subdomains)Use a service like Fleek or Cloudflare to proxy your custom domain to IPFS:
cloudflare-ipfs.com_dnslink.app.yourdapp.com -> dnslink=/ipfs/QmYourCID
Run through every item before sharing the URL publicly.
rm -rf .next out then yarn build completes without errorstrailingSlash: true is set in next.config.jspackages/nextjsscaffold.config.tsdeployedContracts.ts has the production contract addresses.env files are in .gitignore| Problem | Cause | Fix |
|---|---|---|
All routes except / return 404 on IPFS | Missing trailingSlash: true | Add to next.config.js |
| Stale pages in production | Did not clean .next and out before build | rm -rf .next out then rebuild |
| Build works locally, fails on Vercel | Missing env vars or case-sensitive imports | Check Vercel build logs |
| Wallet does not connect | Wrong chain in scaffold.config.ts | Verify targetNetworks |
| "Module not found" on Vercel | Monorepo root directory wrong | Set root to packages/nextjs |
| Images broken on IPFS | Using Next.js Image optimization | Set images: { unoptimized: true } |
| ENS content hash update is expensive | Mainnet gas for ENS update | Budget 0.005-0.02 ETH per update |
| IPFS loads slowly | Using public gateway | Pin content and use dedicated gateway |
| CSS not loading in production | Tailwind purging too aggressively | Check content paths in Tailwind config |
| API routes return 404 on IPFS | IPFS is static hosting — no server | Use output: "export", move API logic onchain or to a separate backend |
Every one of these has cost someone hours of debugging. Do not learn them the hard way.
npx claudepluginhub andginja/ethskills --plugin eth-skillsProvides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.