Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
austintgriffith avatar

Frontend Playbook

  • 35 installs
  • 244 repo stars
  • Updated July 21, 2026
  • austintgriffith/ethskills

frontend-playbook is an Ethereum skill that documents the complete build-to-production deployment pipeline for Scaffold-ETH 2 dApps, including fork mode, IPFS, and Vercel.

About

A build-to-production deployment playbook for Ethereum dApps built on Scaffold-ETH 2. It covers fork-mode local setup, deploying a static frontend to IPFS or Vercel, ENS subdomain setup, and a production checklist. Developers use it when taking a dApp live, especially to avoid IPFS routing 404s, stale builds, and Node 25+ localStorage crashes during static export.

  • Full build-to-production pipeline for Ethereum dApps: fork mode, IPFS deploy, Vercel config, ENS subdomain, production c
  • Documents the fork-vs-chain gotcha (yarn fork --network base gives real protocol state, yarn chain is empty)
  • Covers IPFS routing traps: output export, trailingSlash true, and the Node 25+ localStorage polyfill that breaks static

Frontend Playbook by the numbers

  • 35 all-time installs (skills.sh)
  • Ranked #234 of 479 Web3 & Blockchain skills by installs in the Skillselion catalog
  • Data as of Aug 4, 2026 (Skillselion catalog sync)
At a glance

frontend-playbook capabilities & compatibility

Capabilities
dapp deploy · ipfs deploy · static export
Works with
vercel · github
Use cases
devops · frontend
Runs
Local or remote
Pricing
Free
From the docs

What frontend-playbook says it does

The complete build-to-production pipeline for Ethereum dApps. Fork mode setup, IPFS deployment, Vercel config, ENS subdomain setup, and the full production checklist.
SKILL.md
npx skills add https://github.com/austintgriffith/ethskills --skill frontend-playbook

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs35
repo stars244
Last updatedJuly 21, 2026
Repositoryaustintgriffith/ethskills

What it does

Use when deploying an Ethereum dApp to production via IPFS or Vercel and hitting routing, stale-build, or prerender failures.

Who is it for?

Developers deploying a Scaffold-ETH 2 dApp frontend to IPFS or Vercel in production.

When should I use this skill?

When deploying any Ethereum dApp to production.

What you get

A dApp frontend deploys cleanly to IPFS or Vercel with working routes, correct metadata, and no stale artifacts.

  • Deployed IPFS or Vercel dApp frontend
  • Production checklist pass

By the numbers

  • Node 25+ localStorage polyfill via a single --require file
  • 3 conditions that must be true for IPFS routing

Files

SKILL.mdMarkdownGitHub ↗

Frontend Playbook

What You Probably Got Wrong

"I'll use `yarn chain`." Wrong. yarn chain gives you an empty local chain with no protocols, no tokens, no state. yarn fork --network base gives you a copy of real Base with Uniswap, Aave, USDC, real whale balances — everything (verified addresses: addresses/SKILL.md). Always fork.

"I deployed to IPFS and it works." Did the CID change? If not, you deployed stale output. Did routes work? Without trailingSlash: true, every route except / returns 404. Did you check the OG image? Without NEXT_PUBLIC_PRODUCTION_URL, it points to localhost:3000.

"I'll set up the project manually." Don't. npx create-eth@latest handles everything — Foundry, Next.js, RainbowKit, scaffold hooks. Never run forge init or create Next.js projects from scratch.

---

Fork Mode Setup

Why Fork, Not Chain

yarn chain (WRONG)              yarn fork --network base (CORRECT)
└─ Empty local chain            └─ Fork of real Base mainnet
└─ No protocols                 └─ Uniswap, Aave, etc. available
└─ No tokens                    └─ Real USDC, WETH exist
└─ Testing in isolation         └─ Test against REAL state

Setup

npx create-eth@latest          # Select: foundry, target chain, name
cd <project-name>
yarn install
yarn fork --network base       # Terminal 1: fork of real Base
yarn deploy                    # Terminal 2: deploy contracts to fork
yarn start                     # Terminal 3: Next.js frontend

Critical: Chain ID Gotcha

When using fork mode, the frontend target network MUST be `chains.foundry` (chain ID 31337), NOT the chain you're forking.

The fork runs locally on Anvil with chain ID 31337. Even if you're forking Base:

// scaffold.config.ts during development
targetNetworks: [chains.foundry],  // ✅ NOT chains.base!

Only switch to chains.base when deploying contracts to the REAL network.

Enable Block Mining

# In a new terminal — REQUIRED for time-dependent logic
cast rpc anvil_setIntervalMining 1

Without this, block.timestamp stays FROZEN. Any contract logic using timestamps (deadlines, expiry, vesting) will break silently.

Make it permanent by editing packages/foundry/package.json to add --block-time 1 to the fork script.

---

Deploying to IPFS (Recommended)

IPFS is the recommended deploy path for SE2. Avoids Vercel's memory limits entirely. Produces a fully decentralized static site.

Full Build Command

cd packages/nextjs
rm -rf .next out  # ALWAYS clean first

NEXT_PUBLIC_PRODUCTION_URL="https://yourapp.yourname.eth.link" \
  NODE_OPTIONS="--require ./polyfill-localstorage.cjs" \
  NEXT_PUBLIC_IPFS_BUILD=true \
  NEXT_PUBLIC_IGNORE_BUILD_ERROR=true \
  yarn build

# Upload to BuidlGuidl IPFS
yarn bgipfs upload out
# Save the CID!

Node 25+ localStorage Polyfill (REQUIRED)

Node.js 25+ ships a built-in localStorage object that's MISSING standard WebStorage API methods (getItem, setItem). This breaks next-themes, RainbowKit, and any library that calls localStorage.getItem() during static page generation.

Error you'll see:

TypeError: localStorage.getItem is not a function
Error occurred prerendering page "/_not-found"

The fix: Create polyfill-localstorage.cjs in packages/nextjs/:

if (typeof globalThis.localStorage !== "undefined" &&
    typeof globalThis.localStorage.getItem !== "function") {
  const store = new Map();
  globalThis.localStorage = {
    getItem: (key) => store.get(key) ?? null,
    setItem: (key, value) => store.set(key, String(value)),
    removeItem: (key) => store.delete(key),
    clear: () => store.clear(),
    key: (index) => [...store.keys()][index] ?? null,
    get length() { return store.size; },
  };
}

Why `--require` and not `instrumentation.ts`? Next.js spawns a separate build worker process for prerendering. --require injects into EVERY Node process (including workers). next.config.ts polyfill only runs in the main process. instrumentation.ts doesn't run in the build worker. Only --require works.

IPFS Routing — Why Routes Break

IPFS gateways serve static files. No server handles routing. Three things MUST be true:

1. `output: "export"` in next.config.ts — generates static HTML files.

2. `trailingSlash: true` (CRITICAL) — This is the #1 reason routes break:

  • trailingSlash: false (default) → generates debug.html
  • trailingSlash: true → generates debug/index.html
  • IPFS gateways resolve directories to index.html automatically, but NOT bare filenames
  • Without trailing slash: /debug → 404 ❌
  • With trailing slash: /debugdebug/debug/index.html

3. Pages must survive static prerendering — any page that crashes during yarn build (browser APIs at import time, localStorage) gets skipped silently → 404 on IPFS.

The complete IPFS-safe next.config.ts pattern:

const isIpfs = process.env.NEXT_PUBLIC_IPFS_BUILD === "true";
if (isIpfs) {
  nextConfig.output = "export";
  nextConfig.trailingSlash = true;
  nextConfig.images = { unoptimized: true };
}

SE2's block explorer pages use localStorage at import time and crash during static export. Rename app/blockexplorer to app/_blockexplorer-disabled if not needed.

Stale Build Detection

The #1 IPFS footgun: You edit code, then deploy the OLD build.

# MANDATORY after ANY code change:
rm -rf .next out                     # 1. Delete old artifacts
# ... run full build command ...     # 2. Rebuild from scratch
grep -l "YOUR_STRING" out/_next/static/chunks/app/*.js  # 3. Verify changes present

# Timestamp check:
stat -f '%Sm' app/page.tsx           # Source modified time
stat -f '%Sm' out/                   # Build output time
# Source NEWER than out/ = STALE BUILD. Rebuild first!

The CID is proof: If the IPFS CID didn't change after a deploy, you deployed the same content. A real code change ALWAYS produces a new CID.

Verify Routes After Deploy

ls out/*/index.html                  # Each route has a directory + index.html
curl -s -o /dev/null -w "%{http_code}" -L "https://GATEWAY/ipfs/CID/debug/"
# Should return 200, not 404

---

Deploying to Vercel (Alternative)

SE2 is a monorepo — Vercel needs special configuration.

Configuration

1. Root Directory: packages/nextjs 2. Install Command: cd ../.. && yarn install 3. Build Command: leave default (next build) 4. Output Directory: leave default (.next)

# Via API:
curl -X PATCH "https://api.vercel.com/v9/projects/PROJECT_ID" \
  -H "Authorization: Bearer $VERCEL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"rootDirectory": "packages/nextjs", "installCommand": "cd ../.. && yarn install"}'

Common Failures

ErrorCauseFix
"No Next.js version detected"Root Directory not setSet to packages/nextjs
"cd packages/nextjs: No such file"Build command has cdClear it — root dir handles this
OOM / exit code 129SE2 monorepo exceeds 8GBUse IPFS instead, or vercel --prebuilt

Decision Tree

Want to deploy SE2?
├─ IPFS (recommended) → yarn ipfs / manual build + upload
│   └─ Fully decentralized, no memory limits, works with ENS
├─ Vercel → Set rootDirectory + installCommand
│   └─ Fast CDN, but centralized. May OOM on large projects
└─ vercel --prebuilt → Build locally, push artifacts to Vercel
    └─ Best of both: local build power + Vercel CDN

---

ENS Subdomain Setup

Two mainnet transactions to point an ENS subdomain at your IPFS deployment.

Transaction 1: Create Subdomain (new apps only)

1. Open https://app.ens.domains/yourname.eth 2. Go to "Subnames" tab → "New subname" 3. Enter the label (e.g. myapp) → Next → Skip profile → Open Wallet → Confirm 4. If gas is stuck: switch MetaMask to Ethereum → Activity tab → "Speed up"

Transaction 2: Set IPFS Content Hash

1. Navigate to https://app.ens.domains/myapp.yourname.eth 2. "Records" tab → "Edit Records" → "Other" tab 3. Paste in Content Hash field: ipfs://<CID> 4. Save → Open Wallet → Confirm in MetaMask

For updates to an existing app: skip Tx 1, only do Tx 2.

Verify

# 1. Onchain content hash matches
RESOLVER=$(cast call 0x00000000000C2e074eC69A0dFb2997BA6C7d2e1e \
  "resolver(bytes32)(address)" $(cast namehash myapp.yourname.eth) \
  --rpc-url https://eth.llamarpc.com)
cast call $RESOLVER "contenthash(bytes32)(bytes)" \
  $(cast namehash myapp.yourname.eth) --rpc-url https://eth.llamarpc.com

# 2. Gateway responds (may take 5-15 min for cache)
curl -s -o /dev/null -w "%{http_code}" -L "https://myapp.yourname.eth.link"

# 3. OG metadata correct (not localhost)
curl -s -L "https://myapp.yourname.eth.link" | grep 'og:image'

Use `.eth.link` NOT `.eth.limo`.eth.link works better on mobile.

---

Go to Production — Complete Checklist

When the user says "ship it", follow this EXACT sequence.

Step 1: Final Code Review 🤖

  • All feedback incorporated
  • No duplicate h1, no raw addresses, no shared isLoading
  • scaffold.config.ts has rpcOverrides and pollingInterval: 3000

Step 2: Choose Domain 👤

Ask: "What subdomain do you want? e.g. `myapp.yourname.eth` → `myapp.yourname.eth.link`"

Step 3: Generate OG Image + Fix Metadata 🤖

  • Create 1200×630 PNG (public/thumbnail.png) — NOT the stock SE2 thumbnail
  • Set NEXT_PUBLIC_PRODUCTION_URL to the live domain
  • Verify og:image will resolve to an absolute production URL

Step 4: Clean Build + IPFS Deploy 🤖

cd packages/nextjs && rm -rf .next out
NEXT_PUBLIC_PRODUCTION_URL="https://myapp.yourname.eth.link" \
  NODE_OPTIONS="--require ./polyfill-localstorage.cjs" \
  NEXT_PUBLIC_IPFS_BUILD=true NEXT_PUBLIC_IGNORE_BUILD_ERROR=true \
  yarn build

# Verify before uploading:
ls out/*/index.html                        # Routes exist
grep 'og:image' out/index.html             # Not localhost
stat -f '%Sm' app/page.tsx                 # Source older than out/
stat -f '%Sm' out/

yarn bgipfs upload out                     # Save the CID

Step 5: Share for Approval 👤

Send: "Build ready for review: `https://community.bgipfs.com/ipfs/<CID>`" Wait for approval before touching ENS.

Step 6: Set ENS 🤖

Create subdomain (if new) + set IPFS content hash. Two mainnet transactions.

Step 7: Verify 🤖

  • Content hash matches onchain
  • .eth.link gateway responds with 200
  • OG image loads correctly
  • Routes work (/debug/, etc.)

Step 8: Report 👤

"Live at `https://myapp.yourname.eth.link` — ENS content hash confirmed onchain, unfurl metadata set."

---

Build Verification Process

A build is NOT done when the code compiles. It's done when you've tested it like a real user.

Phase 1: Code QA (Automated)

  • Scan .tsx files for raw address strings (should use <Address/>)
  • Scan for shared isLoading state across multiple buttons
  • Scan for missing disabled props on transaction buttons
  • Verify RPC config and polling interval
  • Verify OG metadata with absolute URLs
  • Verify no public RPCs in any file

Phase 2: Smart Contract Testing

forge test                    # All tests pass
forge test --fuzz-runs 10000  # Fuzz testing

Test edge cases: zero amounts, max amounts, unauthorized callers, reentrancy attempts.

Phase 3: Browser Testing (THE REAL TEST)

Open the app and do a FULL walkthrough:

1. Load the app — does it render correctly? 2. Check page title — is it correct, not "Scaffold-ETH 2"? 3. Connect wallet — does the connect flow work? 4. Wrong network — connect on wrong chain, verify "Switch to Base" appears 5. Switch network — click the switch button, verify it works 6. Approve flow — verify approve button shows, click it, wait for tx, verify action button appears 7. Main action — click primary action, verify loader, wait for tx, verify state updates 8. Error handling — reject a transaction in wallet, verify UI recovers 9. Address displays — all addresses showing ENS/blockies, not raw hex? 10. Share URL — check OG unfurl (image, title, description)

Phase 4: QA Sub-Agent (Complex Builds)

For bigger projects, spawn a sub-agent with fresh context. Give it the repo path and deployed URL. It reads all code against the UX rules, opens a browser, clicks through independently, and reports issues.

---

Don't Do These

  • yarn chain — use yarn fork --network <chain>
  • forge init — use npx create-eth@latest
  • ❌ Manual Next.js setup — SE2 handles it
  • ❌ Manual wallet connection — SE2 has RainbowKit pre-configured
  • ❌ Edit deployedContracts.ts — it's auto-generated by yarn deploy
  • ❌ Hardcode API keys in scaffold.config.ts — use .env.local
  • ❌ Use mainnet.base.org in production — use Alchemy or similar

---

Resources

  • SE2 Docs: https://docs.scaffoldeth.io/
  • UI Components: https://ui.scaffoldeth.io/
  • SpeedRun Ethereum: https://speedrunethereum.com/
  • ETH Tech Tree: https://www.ethtechtree.com
  • BuidlGuidl IPFS: https://upload.bgipfs.com

Related skills

FAQ

Should I use yarn chain or yarn fork?

Always fork. yarn fork --network base gives a copy of real Base with Uniswap, Aave, USDC and real balances, while yarn chain gives an empty local chain with no protocols or tokens.

Why do my routes 404 on IPFS?

The #1 cause is a missing trailingSlash: true, which makes IPFS generate debug.html instead of debug/index.html that gateways can resolve.

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.