> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ravn.exchange/llms.txt
> Use this file to discover all available pages before exploring further.

# @ravn/react

> Hooks for dapps building their own swap UI on top of @ravn/sdk.

Three hooks, one per [`@ravn/sdk`](/sdks/core) call that benefits from React state: quote
fetching with expiry tracking, execute as an async action, and status as a poll. You still bring
your own wallet; these hooks never sign or send anything.

<ParamField body="react" type="peer dependency">Requires React 18 or later.</ParamField>
<ParamField body="@ravn/sdk" type="peer dependency">Create one `RavnClient` and pass it to every hook.</ParamField>

```ts theme={null}
import { RavnClient } from "@ravn/sdk";
import { useRavnQuote, useRavnExecute, useRavnStatus } from "@ravn/react";

const client = new RavnClient(); // create once, pass to every hook below
```

## useRavnQuote

```ts theme={null}
const { quote, isLoading, error, isExpired, refetch } = useRavnQuote(client, {
  inputChainId: 1,
  outputChainId: -2,
  inputToken: "0xEeee...EEeE",
  outputToken: "So1111...1112",
  inputAmount: amount, // e.g. from a controlled input
  userAddress: address,
});
```

Fetches whenever `params` changes (compared by value, not by reference, so a fresh object
literal on every render is fine), and sets `isExpired` on its own timer when `quote.expiresAt`
passes: no polling, one `setTimeout` per quote. Pass `params: null` to skip fetching, for
example when the amount hasn't been entered yet.

<Note>
  Does not auto-refetch on expiry. Silently re-pricing behind the user's back is worse than
  telling them to ask again, so call `refetch()` yourself in response to `isExpired`.
</Note>

<ResponseField name="quote" type="QuoteDTO | null" />

<ResponseField name="isLoading" type="boolean" />

<ResponseField name="error" type="unknown">A `RavnApiError` on a failed request. See [Errors](/sdks/core#errors).</ResponseField>
<ResponseField name="isExpired" type="boolean">True once `quote.expiresAt` has passed. Stop letting the user sign, and call `refetch()`.</ResponseField>

<ResponseField name="refetch" type="() => void" />

## useRavnExecute

```ts theme={null}
const { execute, data, isLoading, error } = useRavnExecute(client);

const handleSwap = async () => {
  const execution = await execute({ quoteToken: quote.quoteToken });
  switch (execution.executionType) {
    case "TRANSACTION": /* ... */ break;
    case "SIGNATURE": /* ... */ break;
    case "DEPOSIT": /* ... */ break;
  }
};
```

A thin state wrapper around `client.execute()`. Branch on the returned `executionType` and hand
it to whatever signer you already have: wagmi, ethers, a hardware wallet. Same contract as the
[Quickstart](/quickstart).

<ResponseField name="execute" type="(params: ExecuteParams) => Promise<ExecutionDTO>">Also throws on failure. Handle the rejection or read `error`, whichever fits your flow.</ResponseField>

<ResponseField name="data" type="ExecutionDTO | null" />

<ResponseField name="isLoading" type="boolean" />

<ResponseField name="error" type="unknown" />

## useRavnStatus

```ts theme={null}
const { status, isLoading, error } = useRavnStatus(
  client,
  execution ? { quoteToken: quote.quoteToken, ref: statusRef } : null,
  4_000 // poll interval in ms, optional, default 4000
);
```

Polls [`GET /status`](/api-reference/status) until the status leaves `pending`/`processing`:
`expired`, `success`, `refunded`, `failed`, `not_found`, and `unknown` all stop the poll. Pass
`params: null` to not poll at all, for example before execution has produced a ref.

<Warning>
  `expired` stops this hook, but it isn't a final outcome. Per [Status](/api-reference/status), a
  refund can still land later and flip it to `refunded`. This hook has no `refetch`, so to catch
  that later transition, call `client.getStatus()` yourself after a delay, or remount the hook.
</Warning>

<Note>
  Keeps polling through a transient network or `5xx` error. Stops immediately on a permanent one,
  `UNAUTHORIZED`, `INVALID_REQUEST`, `QUOTE_INVALID`, or `NOT_FOUND`, since retrying with the same
  arguments can never turn one of those into success.
</Note>

<ResponseField name="status" type="StatusDTO | null" />

<ResponseField name="isLoading" type="boolean" />

<ResponseField name="error" type="unknown" />

## Putting it together

```tsx theme={null}
function SwapButton({ params }: { params: GetQuoteParams }) {
  const { quote, isExpired, refetch } = useRavnQuote(client, params);
  const { execute, data: execution } = useRavnExecute(client);
  const { status } = useRavnStatus(
    client,
    execution?.executionType === "DEPOSIT"
      ? { quoteToken: quote!.quoteToken, ref: execution.statusRef }
      : null
  );

  if (isExpired) return <button onClick={refetch}>Refresh quote</button>;
  return (
    <button onClick={() => execute({ quoteToken: quote!.quoteToken })}>
      {status ? `Status: ${status.status}` : "Swap"}
    </button>
  );
}
```
