Appearance
Transaction Status
Track and monitor transaction status throughout its lifecycle, from submission to confirmation.
Basic Status Tracking
Use useWaitForTransactionReceipt to track transaction confirmation:
tsx
import { useWaitForTransactionReceipt } from "wagmi";
import { useSendTransaction } from "wagmi";
function TransactionTracker() {
const { data: hash, sendTransaction } = useSendTransaction();
const {
isLoading: isConfirming,
isSuccess: isConfirmed,
isError,
data: receipt,
} = useWaitForTransactionReceipt({ hash });
const handleSend = () => {
sendTransaction({
to: "0xA0Cf…251e",
value: parseEther("0.05"),
});
};
return (
<div>
<button onClick={handleSend}>Send Transaction</button>
{hash && (
<div>
<p>Transaction Hash: {hash}</p>
{isConfirming && <p>Waiting for confirmation...</p>}
{isConfirmed && receipt && (
<div>
<p>✅ Transaction confirmed!</p>
<p>Block: {receipt.blockNumber.toString()}</p>
<p>Gas used: {receipt.gasUsed.toString()}</p>
</div>
)}
{isError && <p>❌ Transaction failed</p>}
</div>
)}
</div>
);
}Transaction Lifecycle States
Track all states of a transaction:
tsx
type TransactionState =
| "idle"
| "preparing"
| "signing"
| "pending"
| "confirming"
| "confirmed"
| "failed";
function useTransactionState() {
const [state, setState] = useState<TransactionState>("idle");
const { sendTransaction, isPending, data: hash } = useSendTransaction();
const {
isLoading: isConfirming,
isSuccess,
isError,
} = useWaitForTransactionReceipt({ hash });
useEffect(() => {
if (isPending && !hash) {
setState("signing");
} else if (hash && isConfirming) {
setState("confirming");
} else if (isSuccess) {
setState("confirmed");
} else if (isError) {
setState("failed");
}
}, [isPending, hash, isConfirming, isSuccess, isError]);
const handleTransaction = async () => {
setState("preparing");
try {
await sendTransaction({
to: "0xA0Cf…251e",
value: parseEther("0.05"),
});
} catch (error) {
setState("failed");
}
};
return { state, handleTransaction };
}Polling for Status
Manually poll for transaction status if needed:
tsx
import { usePublicClient } from "wagmi";
function useTransactionPolling(hash: `0x${string}` | undefined) {
const publicClient = usePublicClient();
const [status, setStatus] = useState<"pending" | "confirmed" | "failed">(
"pending",
);
useEffect(() => {
if (!hash || !publicClient) return;
const checkStatus = async () => {
try {
const receipt = await publicClient.getTransactionReceipt({ hash });
if (receipt) {
setStatus("confirmed");
}
} catch (error) {
// Transaction might not be mined yet
setTimeout(checkStatus, 2000); // Poll every 2 seconds
}
};
checkStatus();
}, [hash, publicClient]);
return status;
}Transaction Receipt Details
Access detailed receipt information:
tsx
function TransactionDetails({ hash }: { hash: `0x${string}` }) {
const { data: receipt, isLoading } = useWaitForTransactionReceipt({ hash });
if (isLoading) {
return <div>Loading transaction details...</div>;
}
if (!receipt) {
return <div>Transaction not found</div>;
}
return (
<div>
<h3>Transaction Details</h3>
<p>Status: {receipt.status === "success" ? "Success" : "Failed"}</p>
<p>Block Number: {receipt.blockNumber.toString()}</p>
<p>Block Hash: {receipt.blockHash}</p>
<p>Gas Used: {receipt.gasUsed.toString()}</p>
<p>Effective Gas Price: {receipt.gasPrice?.toString()}</p>
<p>Transaction Hash: {receipt.transactionHash}</p>
<p>From: {receipt.from}</p>
<p>To: {receipt.to}</p>
</div>
);
}Error handling
Transaction confirmation failures (e.g. revert) are exposed via useWaitForTransactionReceipt's isError and error. Use them to show a failure message or retry. For user rejection and send-time errors, see Best practices — Error handling.
Status UI Component
Create a reusable transaction status component:
tsx
function TransactionStatus({
hash,
onSuccess,
}: {
hash: `0x${string}` | undefined;
onSuccess?: () => void;
}) {
const {
isLoading,
isSuccess,
isError,
data: receipt,
} = useWaitForTransactionReceipt({ hash });
useEffect(() => {
if (isSuccess && onSuccess) {
onSuccess();
}
}, [isSuccess, onSuccess]);
if (!hash) return null;
if (isLoading) {
return (
<div className="transaction-status pending">
<Spinner />
<p>Transaction pending...</p>
<p className="hash">{hash}</p>
</div>
);
}
if (isError) {
return (
<div className="transaction-status error">
<p>❌ Transaction failed</p>
<p className="hash">{hash}</p>
</div>
);
}
if (isSuccess && receipt) {
return (
<div className="transaction-status success">
<p>✅ Transaction confirmed!</p>
<p>Block: {receipt.blockNumber.toString()}</p>
<p className="hash">{hash}</p>
</div>
);
}
return null;
}Best Practices
- Always track status: Use
useWaitForTransactionReceiptto monitor transaction confirmation - Show user feedback: Display clear status messages at each stage
- Handle errors gracefully: Provide actionable error messages
- Display transaction hash: Allow users to view their transaction on block explorers
- Call callbacks on success: Use
useEffectto trigger actions when transaction confirms
Block Explorer Links
Link to block explorers for transaction details:
tsx
function TransactionLink({
hash,
chainId,
}: {
hash: `0x${string}`;
chainId: number;
}) {
const explorerUrl =
chainId === 42220
? `https://celoscan.io/tx/${hash}` // Celo mainnet
: `https://sepolia.celoscan.io/tx/${hash}`; // Celo Sepolia
return (
<a href={explorerUrl}>
View on CeloScan
</a>
);
}Next Steps
- Learn about sending transactions
- See best practices for wallet interactions
- Check out example implementations