A useful first Solana app should connect a wallet, read live network state, and send a transaction. A static wallet button proves little.
The current official Next.js starter gives you those working parts. It uses @solana/kit v7, @solana/react, Wallet Standard discovery, and plugin clients for common programs. By the end, you will run the app on devnet, fund a wallet, transfer SOL, and know which files own each responsibility.
Start from the official Next.js template
The Solana Foundation maintains a Next.js template through create-solana-dapp. Starting there avoids copying provider code from an older tutorial.
You need Node.js and a browser wallet that supports Wallet Standard. The template page currently lists Next.js, Tailwind CSS v4, Kit v7, and @solana/react.
npx -y create-solana-dapp@latest \
-t solana-foundation/templates/kit/nextjs
cd your-project-name
npm install
npm run devOpen http://localhost:3000. The generated interface can connect a wallet, change clusters, request devnet SOL, transfer SOL, create and mint tokens, and add memos.
The starter includes more surface than a beginner needs. Retain it for the first run because every working action becomes a reference when you build your own feature.
Verify the baseline before editing
Run the Starter Verification Loop:
- Connect a browser wallet.
- Select devnet in the network control.
- Request 1 devnet SOL from the included airdrop action or the official faucet.
- Refresh the page and confirm the balance still matches the network.
- Send a small transfer to another devnet address.
- Open the returned signature in Solana Explorer.
The final Explorer record proves that the complete path works: browser wallet, Kit client, RPC submission, and cluster confirmation.
Understand the client before changing the UI
The starter builds one Kit client for the selected Solana cluster. Its client setup composes plugins in a specific order:
createClient()
.use(walletSigner({ chain }))
.use(solanaRpc({ rpcUrl, rpcSubscriptionsUrl }))
.use(rpcAirdrop())
.use(systemProgram())
.use(tokenProgram())
.use(memoProgram());The wallet signer comes before the RPC plugin because later actions need an identity and fee payer. The RPC plugin supplies requests, subscriptions, balance reads, and transaction delivery. Program plugins then add typed instructions and action plans.
ClientProvider from @solana/react makes that client available to React components. A component reads it with useClient(). Wallet hooks from @solana/kit-plugin-wallet/react expose discovery, connection, the active wallet, and disconnect behavior.
This structure gives each layer one job:
- The client file selects chain capabilities.
- The provider owns the current client instance.
- Wallet hooks own the browser session.
- Feature components build and send actions.
- Solana programs enforce the on-chain rules.
When you switch networks, the provider creates a client bound to the new chain and RPC endpoints. Discard addresses and cached balances from the previous cluster.
Build one small feature all the way through
Begin with a SOL transfer. It has a visible input, a wallet approval, a signature, and a balance change. The entire system becomes observable.
Inside a client component, get the Kit client and connected wallet. Validate the recipient and amount before you prepare an instruction. The template's system plugin exposes the transfer instruction and a send path.
The exact generated helper names can change between plugin versions. Read the template's existing transfer component and its pinned package versions before extracting your own component. The stable pattern is:
const client = useClient();
const wallet = useConnectedWallet();
if (!wallet) {
throw new Error('Connect a wallet before sending.');
}
const instruction = client.system.instructions.transferSol({
destination,
amount,
});
const signature = await client.sendTransaction([instruction]);Treat this as an architecture sketch rather than a substitute for the generated, version-pinned component. TypeScript should decide whether your installed plugin expects different field names or value types.
Give the transaction real interface states
A transaction button needs more than idle and success. Model these states:
- disconnected: explain that a wallet connection is required.
- invalid: identify the recipient or amount problem.
- ready: show the network and action before signing.
- signing: wait for wallet approval without creating another request.
- submitted: retain and display the signature.
- confirmed: show the Explorer link and refresh affected state.
- failed: keep the error and a safe recovery action.
- expired: rebuild with a current blockhash before another signature.
Disable duplicate submission while a request is active. A browser timeout does not prove that a transaction failed. Query the signature before preparing another transfer.
For the complete retry and confirmation model, use the Builderz transaction reliability runbook.
Pick one React integration path
The current Solana documentation presents two modern React approaches.
The official Next.js template uses Kit v7, @solana/react, and plugin clients. This path is useful when you want program plugins and a generated app that already sends several transaction types.
The frontend documentation also offers @solana/client with @solana/react-hooks. This package provides a shared client runtime with hooks for wallets, balances, transfers, signatures, and queries. The official documentation recommends the hooks layer as a fast starting point for a React-only interface.
Both approaches sit on modern Solana tooling. Their providers, hooks, and action APIs are different. Choose one path for a feature boundary and follow its current documentation. Mixing examples from both paths usually produces duplicate providers, incompatible types, or wallet state that does not agree across components.
Legacy applications still using @solana/web3.js need a migration plan rather than a blind rewrite. Solana documents @solana/web3-compat as the compatibility route for projects that cannot move every dependency at once.
Move from devnet demo to production app
Devnet proves the interaction, not the operating model. Before mainnet, replace tutorial defaults with explicit product decisions.
Control RPC configuration
Store RPC HTTP and WebSocket endpoints in validated environment configuration. Expose only public client-safe values to browser code. Never place a signing secret in a NEXT_PUBLIC_ variable.
Public cluster endpoints are useful for learning. A production application needs capacity, rate limits, geographic behavior, and support that match its traffic.
Keep signing in the right boundary
Browser wallets should sign user-authorized transactions. Backend automation should use a key-management service or another controlled signer. Never paste a production keypair into a source file, API route, or deployment variable without a key lifecycle and access policy.
Confirm outcomes instead of trusting submission
A returned signature identifies the submitted transaction. Preserve it, show it to the user, and track confirmation at the commitment your product requires.
After confirmation, refresh the relevant account or balance. The optimistic interface state is not proof of chain state.
Test the failure path
Run the feature with a rejected wallet request, an invalid address, insufficient funds, a slow RPC, an expired blockhash, and a page refresh during confirmation. A working happy path is the start of the test plan.
Common setup failures have short fixes
No wallet appears
Install or enable a Wallet Standard compatible browser wallet. Confirm the component renders inside the client provider and runs on the client, not as a React Server Component.
The airdrop fails
Check that the selected cluster is devnet. Airdrops are unavailable on mainnet and may be rate limited. Try the official Solana faucet if the template action is temporarily limited.
The balance never changes
Confirm the wallet address and cluster match the Explorer view. A devnet balance will not appear on mainnet. Check that subscriptions reconnect after a network change and use an RPC read as the recovery path.
TypeScript rejects copied code
Compare the guide against your installed package versions. Kit and its plugins expose strong types, so a mismatch often means the example came from another release or integration path.
The page hydrates with an error
Wallet discovery needs browser APIs. Place wallet controls and provider code in client components. Pass serializable data across the Server Component boundary.
FAQ
Is `@solana/kit` the replacement for `@solana/web3.js`?
Kit is the renamed modern 2.x line and now has newer major versions. Solana marks @solana/web3.js as deprecated in its current frontend guidance and provides @solana/web3-compat for staged migrations.
Should a new Next.js app use React hooks or Kit plugins?
Choose the official Kit v7 Next.js template when its plugin model fits your app. Prefer the documented @solana/react-hooks path when you want its client runtime and hook APIs. Avoid mixing both provider models in one feature.
Can a Server Component connect a wallet?
No. Wallet discovery and approval depend on browser APIs. Put that interaction in a client component while using Server Components for static content and server-owned data.
Do I need an Anchor program for this tutorial?
No. The starter can send SOL, token, and memo instructions through existing Solana programs. Add Anchor when your product needs custom on-chain rules.
Can I use the devnet RPC in production?
Devnet is for development and testing. A production app uses mainnet endpoints and an RPC plan sized for its reliability, traffic, and support requirements.
Primary references
- Official Solana Next.js template
- Solana frontend libraries
- Solana @solana/client guide
- Anza Solana Kit repository
- Create Solana dApp
- Solana devnet faucet
Next step: If your starter needs custom programs, transaction design, or production RPC architecture, book a technical scoping call through the Builderz project fit form.



