Creating a React minting app
The repository includes a complete example at examples/rainbowkit-mint, demonstrating how to implement minting with Manifold products including Edition Products
Overview
The edition RainbowKit example showcases how to:
Connect wallets with RainbowKit + wagmi
Mint Edition products through the Manifold Client SDK
Run on Next.js 14 with the App Router and TypeScript
Display mint progress, costs, and errors in the UI
Complete example at examples/rainbowkit-mint
Quick start
Install workspace dependencies
pnpm installCreate environment variables
cp .env.example \ env.localFill in:
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_walletconnect_project_id NEXT_PUBLIC_INSTANCE_ID=your_edition_instance_id NEXT_PUBLIC_RPC_URL_SEPOLIA=your_alchemy_rpc_urlNEXT_PUBLIC_INSTANCE_IDmust point to an Edition product you published in Manifold Studio.NEXT_PUBLIC_WALLETCONNECT_PROJECT_IDis optional but required if you want to support WalletConnect wallets.NEXT_PUBLIC_RPC_URL_SEPOLIAyour Alchemy Sepolia RPC URL.Launch the example
pnpm devVisit http://localhost:3000 and connect a wallet with RainbowKit’s ConnectButton.
Key implementation steps
Ensure you have the ConnectButton component on your page. This handles wallet connections, which are required to create an Account that the SDK uses for checks and transaction execution.
'use client';
import { ConnectButton } from '@rainbow-me/rainbowkit';
export default function Home() {
return (
<main>
<div>
<ConnectButton />
</div>
</main>
);
}Implement Minting Logic in MintButton.tsx
'use client';
import { ConnectButton } from '@rainbow-me/rainbowkit';
import { MintButton } from '@/components/MintButton';
export default function Home() {
return (
<main>
<h1>
Manifold SDK + RainbowKit
</h1>
<div>
<ConnectButton />
<MintButton />
</div>
</main>
);
}Core Steps
a. Create a Manifold Client with a public provider
import { createClient, createPublicProviderWagmi } from '@manifoldxyz/client-sdk';
import { useConfig } from 'wagmi';
// Get the Wagmi config from your React context
const config = useConfig();
// Create a public provider using Wagmi config
const publicProvider = createPublicProviderWagmi({ config });
// Initialize the client
const client = createClient({ publicProvider });b. Create an Account representing the connected user.
import { createAccountViem } from '@manifoldxyz/client-sdk';
import { useWalletClient } from 'wagmi';
// Get the wallet client from wagmi
const { data: walletClient } = useWalletClient();
// Create the account adapter
const account = createAccountViem({
walletClient,
});c. Fetch the product and verify its type
const product = await client.getProduct(INSTANCE_ID) as EditionProduct;d. Check the product status to ensure it’s still active
const productStatus = await product.getStatus();
if (productStatus !== 'active') {
throw new Error(`Product is ${productStatus}`);
}e. Prepare the purchase by specifying the amount
const preparedPurchase = await product.preparePurchase({
address: address,
payload: {
quantity: 1,
},
});f. Execute the purchase
const order = await product.purchase({
account,
preparedPurchase,
});Key points:
createAccountViemwraps wagmi’s wallet client so the SDK can sign and send transactions on the user’s behalf.preparePurchaseperforms all eligibility checks (allowlists, supply, promo codes) and returns the total cost breakdown. Supply the sameaccountso balance checks run against the connected wallet.purchaseexecutes the transaction sequence (ERC-20 approvals, mint, etc.) and returns a Receipt with the final transaction hash and minted tokens.
Display token media and on-chain stats
You can enrich the UI with product art and live supply data directly from the SDK:
const product = await client.getProduct(instanceId);
// Off-chain media and metadata (safe to render immediately)
const { asset, title, contract } = product.data.publicData;
const imageUrl = asset.image ?? asset.imagePreview;
const animationUrl = asset.animation ?? asset.animationPreview;
// Fetch on-chain data once (cost, supply, timing)
const onchainData = await product.fetchOnchainData();
const { totalMinted, totalSupply, startDate, endDate, cost } = onchainData;
return (
<section>
{imageUrl && <img src={imageUrl} alt={title} />}
{animationUrl && (
<video src={animationUrl} autoPlay loop muted playsInline />
)}
<dl>
<dt>Price</dt>
<dd>{cost.formatted}</dd>
<dt>Minted</dt>
<dd>{totalMinted}</dd>
<dt>Total supply</dt>
<dd>{totalSupply === -1 ? 'Unlimited' : totalSupply}</dd>
<dt>Start date</dt>
<dd>{startDate?.toLocaleString() ?? 'TBD'}</dd>
<dt>End date</dt>
<dd>{endDate?.toLocaleString() ?? 'Open'}</dd>
<dt>Contract</dt>
<dd>{contract.address}</dd>
</dl>
</section>
);Best practices:
Check status with getStatus before attempting a purchase to verify the product is active.
Handle ClientSDKError codes for common cases such as ineligibility, sold-out items, or insufficient funds.
Call
getAllocationswhen you need to show remaining allowlist spots.Inspect
Receipt.orderto display which tokens were minted afterpurchase.
Last updated