React

The Aave React SDK is a collection of React hooks for building decentralized applications on top of the Aave Protocol. It provides a simple and type-safe way to interact with Aave markets, manage user positions, and execute transactions.

Getting Started

To get started, follow the steps below.

1

Install SDK

First, install the Aave React SDK packages using your package manager of choice.

npm install @aave/react@latest

2

Setup Client

Then, create an AaveClient instance that will be used to interact with the protocol.

client.ts
import { AaveClient } from "@aave/react";
export const client = AaveClient.create();

You don't need to install the @aave/client package as it's already included and re-exported by the @aave/react package.

3

Setup Provider

Next, wrap your app with the <AaveProvider> component and pass the client instance.

App.tsx
import { AaveProvider } from "@aave/react";
import { client } from "./client";
export function App() {  return (    <AaveProvider client={client}>      {/* Your application components */}    </AaveProvider>  );}

4

Start Building

That's it—you can now start using the Aave React SDK.

Example
import { useAaveChains } from "@aave/react";
export function SupportedChains() {  const { data: chains } = useAaveChains();
  return (    <div>      <h2>Supported Chains</h2>      {chains?.map((chain) => (        <div key={chain.id}>          <h3>{chain.name}</h3>          <p>Chain ID: {chain.id}</p>        </div>      ))}    </div>  );}

Integrations

The Aave React SDK includes first-class support for viem, ethers v6, Privy, Dynamic and thirdweb.

Ensure you have viem package installed in your project.

npm install viem@2

Send Aave Transactions

To send transactions through the connected wallet, use the useSendTransaction action hook.

const [sendTransaction, { loading, error }] = useSendTransaction(/* args */);

This hook abstracts the wallet interaction and provides a simple interface for submitting transactions.

Import the useSendTransaction hook from the @aave/react/viem entry point and wire it up with the viem's WalletClient.

Viem
import { useSendTransaction } from "@aave/react/viem";import { useWalletClient } from "wagmi";
function MyComponent() {  const { data: walletClient } = useWalletClient();  const [sendTransaction, sending] = useSendTransaction(walletClient);
  // Use with transaction hooks…}

The example uses wagmi to get the WalletClient from the connected wallet.

Then, use it as describe in the specific page.

Sign ERC-20 Permits

Aave makes use of EIP-2612 to allow users to avoid ERC-20 approval before sending transactions.

Use the useERC20Permit hook to sign ERC-20 permits.

Import the useERC20Permit hook from the @aave/react/viem entry point and wire it up with the viem's WalletClient.

Viem
import { useERC20Permit } from "@aave/react/viem";import { useWalletClient } from "wagmi";
function MyComponent() {  const { data: walletClient } = useWalletClient();  const [sendTransaction, sending] = useERC20Permit(walletClient);
  // Use with transaction hooks…}

The example uses wagmi to get the WalletClient from the connected wallet.

Then, use it as describe in the specific page.

Read Hooks

All read hooks (market data, user positions, etc.) support two ways of operating: loading state and React Suspense.

Loading State

Handle loading state manually in your component:

Loading States
function ChainsList() {  const { data, loading, error } = useAaveChains();
  if (loading) return <div>Loading…</div>;
  if (error) return <div>Error: {error.message}</div>;
  return (    <div>      {data.map((chain) => (        <div key={chain.id}>{chain.name}</div>      ))}    </div>  );}

React Suspense

Let React handle loading states automatically through a Suspense boundary.

With Suspense
// Component - no loading states neededfunction ChainsList() {  const { data: chains } = useAaveChains({    suspense: true, // Enable suspense mode  });
  return (    <div>      {chains.map((chain) => (        <div key={chain.id}>{chain.name}</div>      ))}    </div>  );}
// Parent - wrap with Suspense boundaryfunction ChainsPage() {  return (    <Suspense fallback={<div>Loading …</div>}>      <ChainsList />    </Suspense>  );}

Action Hooks

The Aave React SDK provides action hooks that are designed to be triggered manually when you need to perform an operation. Action hooks return an execute function and a state object.

const [execute, state] = useActionHook();

Execute Function

The execute function returns a ResultAsync<T, E>, a thenable object that represents one of two states:

  • Ok<T>: A successful result containing a value of type T

  • Err<E>: A failure containing an error of type E

Awaiting a ResultAsync<T, E> resolves to a Result<T, E>.

With a Result<T, E>, you can use the convenient isOk() and isErr() methods to check the outcome and narrow the type.
If you have a ResultAsync<T, E>, you must first await it to access those methods.

const result = await execute();
if (result.isOk()) {  console.log("Result:", result.value);} else {  console.error("Error:", result.error);}

This approach avoids reliance on try/catch blocks and promotes predictable, type-safe code by ensuring errors are handled explicitly.

See the NeverThrow documentation for more information. The NeverThrow library is re-exported for convenience.

State Object

The state object returned by action hooks provides information about the current operation status:

const { called, data, error, loading } = state;

Where:

  • called: true when the operation has been executed at least once, false otherwise

  • data: Contains the last successful result of type T, undefined otherwise

  • error: Contains the error of type E if the operation failed, undefined otherwise

  • loading: true when the operation is in progress, false otherwise

Transaction Hooks

Transaction hooks are a specific type of action hook that handle protocol interactions like supply, borrow, withdraw, repay, and more.

There are two types of transaction hooks:

  • Simple transactions - single transactions that can be sent directly to the wallet

  • Complex transactions - transactions that could require approval beforehand

Simple Transactions

To handle a simple transaction hook, follow the steps below.

1

Use Hooks

First, instantiate the transaction hook and the useSendTransaction hook according to the wallet provider you are using.

Instantiate Hooks
const [prepare, preparing] = useSimpleTransaction();const [sendTransaction, sending] = useSendTransaction(/* … */);

2

Execute the Transaction

Next, execute the transaction in your app by chaining the prepare and send operations.

const result = await prepare(/* args */).andThen(sendTransaction);

Optionally, combine the state objects to drive your UI components.

const loading = preparing.loading && sending.loading;const error = preparing.error || sending.error;

3

Handle the Result

Finally, handle the result of the operation.

if (result.isErr()) {  switch (result.error.name) {    case "SigningError":      // Most likely the user rejected the transaction      console.error(`Failed to sign the transaction: ${error.message}`);      break;
    case "TimeoutError":      console.error(`Transaction timed out: ${error.message}`);      break;
    case "TransactionError":      console.error(`Transaction failed: ${error.message}`);      break;
    case "UnexpectedError":      console.error(error.message);      break;  }} else {  console.log("Transaction sent with hash:", result.value);}

Complex Transactions

To handle a complex transaction hook, follow the steps below.

1

Use Hooks

First, instantiate the transaction hook and the useSendTransaction hook according to the wallet provider you are using.

Instantiate Hooks
const [prepare, preparing] = useComplexTransaction();const [sendTransaction, sending] = useSendTransaction(/* … */);

Optionally, combine the state objects to drive your UI components.

const loading = preparing.loading && sending.loading;const error = preparing.error || sending.error;

2

Process the Execution Plan

Next, handle the execution plan in your transaction flow. Some operations may require token approval before executing the main transaction.

import { errAsync } from "@aave/react";
const result = await prepare(/* args */).andThen((plan) => {  switch (plan.__typename) {    case "TransactionRequest":      return sendTransaction(plan);
    case "ApprovalRequired":      return sendTransaction(plan.approval).andThen(() =>        sendTransaction(plan.originalTransaction)      );
    case "InsufficientBalanceError":      return errAsync(        new Error(`Insufficient balance: ${plan.required.value} required.`)      );  }});
// …

If you wish to ask the user for confirmation before sending the transaction, you can do so by integrating your app's confirmation UI at this point.

3

Handle the Result

Finally, handle the result of the operation.

if (result.isErr()) {  switch (result.error.name) {    case "SigningError":      // Most likely the user rejected the transaction      console.error(`Failed to sign the transaction: ${error.message}`);      break;
    case "TimeoutError":      console.error(`Transaction timed out: ${error.message}`);      break;
    case "TransactionError":      console.error(`Transaction failed: ${error.message}`);      break;
    case "UnexpectedError":      console.error(error.message);      break;  }} else {  console.log("Transaction sent with hash:", result.value);}

Next Steps