diff --git a/docs/apps/quickstart/build-app.mdx b/docs/apps/quickstart/build-app.mdx
new file mode 100644
index 000000000..0dd405fc4
--- /dev/null
+++ b/docs/apps/quickstart/build-app.mdx
@@ -0,0 +1,547 @@
+---
+title: "Build an app on Base"
+description: "A step-by-step guide to building a Next.js tally app on Base using wagmi and viem, with wallet connection, contract reads and writes, and batch transaction support."
+---
+
+This guide walks you through building an onchain tally app on Base from scratch. You will connect wallets, read and write to a smart contract, detect wallet capabilities, and fall back gracefully for wallets that do not support batching.
+
+## What you'll build
+
+- A Next.js app that connects wallets and handles connection state
+- Contract reads and writes against a deployed counter on Base Sepolia
+- Batch transaction support for smart wallets via EIP-5792
+- A graceful fallback for wallets that do not support batching
+
+
+Base is a fast, low-cost Ethereum L2 built to bring the next billion users onchain. Low gas fees make batch transactions practical and real-time UX possible. Every pattern in this guide works on any EVM chain.
+
+
+## Steps
+
+
+
+ Create a new Next.js app and install the required dependencies.
+
+ ```bash Terminal
+ npx create-next-app@latest my-base-app --typescript --tailwind --app
+ cd my-base-app
+ npm install wagmi viem @tanstack/react-query @base-org/account
+ ```
+
+
+
+ Create the Wagmi config with Base Sepolia, then wrap your app in the required providers.
+
+ ```typescript config/wagmi.ts lines expandable
+ import { http, createConfig, createStorage, cookieStorage } from 'wagmi'
+ import { baseSepolia } from 'wagmi/chains'
+ import { baseAccount, injected } from 'wagmi/connectors'
+
+ export const config = createConfig({
+ chains: [baseSepolia],
+ connectors: [
+ injected(),
+ baseAccount({
+ appName: 'My Base App',
+ }),
+ ],
+ storage: createStorage({ storage: cookieStorage }),
+ ssr: true,
+ transports: {
+ [baseSepolia.id]: http('https://sepolia.base.org'),
+ },
+ })
+
+ declare module 'wagmi' {
+ interface Register {
+ config: typeof config
+ }
+ }
+ ```
+
+
+ `ssr: true` combined with `cookieStorage` prevents Next.js hydration mismatches. The `baseAccount` connector connects users via the [Base Account SDK](/base-account/overview/what-is-base-account) smart wallet — you will detect its capabilities in step 7. The `injected` connector handles browser extension wallets like MetaMask.
+
+
+ ```typescript app/providers.tsx lines expandable
+ 'use client'
+
+ import { WagmiProvider } from 'wagmi'
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+ import { type ReactNode } from 'react'
+ import { config } from '@/config/wagmi'
+
+ const queryClient = new QueryClient()
+
+ export function Providers({ children }: { children: ReactNode }) {
+ return (
+
+
+ {children}
+
+
+ )
+ }
+ ```
+
+ Wrap your root layout with ``.
+
+
+
+ Create a component that handles all four wallet connection states.
+
+ ```typescript components/ConnectWallet.tsx lines expandable
+ 'use client'
+
+ import { useAccount, useConnect, useDisconnect } from 'wagmi'
+
+ export function ConnectWallet() {
+ const { address, isConnected, isConnecting, isReconnecting } = useAccount()
+ const { connect, connectors } = useConnect()
+ const { disconnect } = useDisconnect()
+
+ if (isReconnecting) return
+ )
+ }
+ ```
+
+
+ `useReadContract` caches its result and does not automatically refetch after a write. Use `queryClient.invalidateQueries` with the read's query key to trigger a single refetch when a transaction confirms.
+
+
+ Surface three states to the user: waiting for wallet signature, waiting for on-chain confirmation, and success.
+
+
+ Without `useSwitchChain`, calling `writeContract` while the wallet is on the wrong network causes wagmi to attempt a background chain switch. If the user misses or dismisses the wallet popup, the button stays at "Confirm in Wallet..." indefinitely with no error and no recovery path.
+
+
+
+
+ Smart wallets support batch transactions via EIP-5792. EOAs do not. Detect support before attempting to batch.
+
+ ```typescript hooks/useWalletCapabilities.ts lines expandable
+ import { useCapabilities } from 'wagmi'
+ import { baseSepolia } from 'wagmi/chains'
+ import { useMemo } from 'react'
+
+ export function useWalletCapabilities() {
+ const { data: capabilities } = useCapabilities()
+
+ const supportsBatching = useMemo(() => {
+ const atomic = capabilities?.[baseSepolia.id]?.atomic
+ return atomic?.status === 'ready' || atomic?.status === 'supported'
+ }, [capabilities])
+
+ const supportsPaymaster = useMemo(() => {
+ return capabilities?.[baseSepolia.id]?.paymasterService?.supported === true
+ }, [capabilities])
+
+ return { supportsBatching, supportsPaymaster }
+ }
+ ```
+
+
+ `useChainId()` returns the wallet's current chain, not your deployment chain. A MetaMask user on Ethereum mainnet would get incorrect capability results. Always check capabilities against the chain where your contract is deployed.
+
+
+ See [Batch Transactions with Wagmi](/base-account/framework-integrations/wagmi/batch-transactions) for a deeper look at EIP-5792 capability detection.
+
+
+
+ Use `useSendCalls` for smart wallets and `useWriteContract` for EOAs. The component detects which path to take at render time.
+
+ ```typescript components/BatchIncrement.tsx lines expandable
+ 'use client'
+
+ import { useEffect } from 'react'
+ import {
+ useSendCalls,
+ useWaitForCallsStatus,
+ useWriteContract,
+ useWaitForTransactionReceipt,
+ useAccount,
+ useChainId,
+ useSwitchChain,
+ } from 'wagmi'
+ import { readContractQueryOptions } from 'wagmi/query'
+ import { useQueryClient } from '@tanstack/react-query'
+ import { encodeFunctionData } from 'viem'
+ import { baseSepolia } from 'wagmi/chains'
+ import { config } from '@/config/wagmi'
+ import { useWalletCapabilities } from '@/hooks/useWalletCapabilities'
+ import { COUNTER_ADDRESS, counterAbi } from '@/config/counter'
+
+ const counterQueryKey = readContractQueryOptions(config, {
+ address: COUNTER_ADDRESS,
+ abi: counterAbi,
+ functionName: 'number',
+ chainId: baseSepolia.id,
+ }).queryKey
+
+ export function BatchIncrement() {
+ const { isConnected } = useAccount()
+ const { supportsBatching } = useWalletCapabilities()
+
+ if (!isConnected) return
+ )
+ }
+
+ function SequentialFlow() {
+ const chainId = useChainId()
+ const { switchChain, isPending: isSwitching } = useSwitchChain()
+ const { data: hash, isPending, writeContract } = useWriteContract()
+ const { isLoading: isConfirming, isSuccess } =
+ useWaitForTransactionReceipt({ hash })
+ const queryClient = useQueryClient()
+
+ useEffect(() => {
+ if (isSuccess) {
+ queryClient.invalidateQueries({ queryKey: counterQueryKey })
+ }
+ }, [isSuccess, queryClient])
+
+ if (chainId !== baseSepolia.id) {
+ return (
+
+ )
+ }
+
+ return (
+
+ )
+ }
+ ```
+
+
+ Never call `useSendCalls` without first confirming `supportsBatching` is `true`. Calling it against an EOA will throw.
+
+
+
+
+ Compose the components into a single page.
+
+ ```typescript app/page.tsx lines expandable
+ import { ConnectWallet } from '@/components/ConnectWallet'
+ import { CounterDisplay } from '@/components/CounterDisplay'
+ import { BatchIncrement } from '@/components/BatchIncrement'
+
+ export default function Home() {
+ return (
+
+
Onchain Tally
+
+
+
+
+ )
+ }
+ ```
+
+ Start the development server.
+
+ ```bash Terminal
+ npm run dev
+ ```
+
+
+
+## Next steps
+
+- **Go to mainnet** — add `base` to your `chains` array and transports in `config/wagmi.ts`, redeploy your contract to Base mainnet, and update `COUNTER_ADDRESS`.
+- **Sponsor gas** — use the `paymasterService` capability with `useSendCalls` to cover your users' transaction fees. See [Sponsor Gas](/base-account/improve-ux/sponsor-gas/paymasters).
+- **Send notifications** — use the [Notifications guide](/apps/technical-guides/base-notifications) to fetch opted-in wallet addresses and send in-app notifications.
+- **Batch read calls** — reduce RPC round trips by batching reads via viem's `multicall`.
+- **Optimistic updates** — update the UI before confirmation using TanStack Query's `onMutate` callback.
+- **Wagmi setup reference** — review the full [Wagmi setup guide](/base-account/framework-integrations/wagmi/setup) for additional configuration options.