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

# Borrow widget

> Turn on Borrow in @stakekit/widget 0.0.290: two config props, two adapter methods if your app manages the wallet, market scope, testing and troubleshooting.

export const ExpandableDemo = ({src, title, desktopWidth = 1440, desktopHeight = 900, caption}) => {
  const [open, setOpen] = useState(false);
  const [scale, setScale] = useState(0.5);
  const boxRef = useRef(null);
  useEffect(() => {
    const box = boxRef.current;
    if (!box) return undefined;
    const update = () => setScale(Math.min(1, box.clientWidth / desktopWidth));
    update();
    const observer = new ResizeObserver(update);
    observer.observe(box);
    return () => observer.disconnect();
  }, [desktopWidth]);
  const dialogRef = useRef(null);
  const closeRef = useRef(null);
  useEffect(() => {
    const dialog = dialogRef.current;
    if (!dialog) return undefined;
    if (open && !dialog.open) {
      dialog.showModal();
      closeRef.current?.focus();
    }
    if (!open && dialog.open) dialog.close();
    const previousOverflow = document.body.style.overflow;
    if (open) document.body.style.overflow = "hidden";
    return () => {
      document.body.style.overflow = previousOverflow;
    };
  }, [open]);
  return <figure style={{
    margin: "24px 0"
  }}>
      <div ref={boxRef} style={{
    position: "relative",
    width: "100%",
    height: `${Math.round(desktopHeight * scale)}px`,
    overflow: "hidden",
    borderRadius: "16px",
    border: "1px solid rgba(127, 127, 127, 0.25)"
  }}>
        <iframe src={src} title={`${title} (preview)`} width={desktopWidth} height={desktopHeight} loading="lazy" tabIndex={-1} aria-hidden="true" style={{
    border: 0,
    width: `${desktopWidth}px`,
    height: `${desktopHeight}px`,
    transform: `scale(${scale})`,
    transformOrigin: "0 0",
    pointerEvents: "none"
  }} />
        <button type="button" onClick={() => setOpen(true)} aria-label={`Open the ${title} live demo`} style={{
    position: "absolute",
    inset: 0,
    width: "100%",
    height: "100%",
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    background: "rgba(0, 0, 0, 0.12)",
    border: 0,
    cursor: "pointer"
  }}>
          <span style={{
    padding: "10px 18px",
    borderRadius: "999px",
    background: "#111827",
    color: "#ffffff",
    fontSize: "15px",
    fontWeight: 600,
    boxShadow: "0 4px 16px rgba(0, 0, 0, 0.25)"
  }}>
            Open live demo
          </span>
        </button>
      </div>
      <figcaption style={{
    marginTop: "8px",
    fontSize: "14px",
    opacity: 0.75,
    textAlign: "center"
  }}>
        {caption ? `${caption} ` : ""}
        <a href={src} target="_blank" rel="noopener noreferrer">
          Open in a new tab
        </a>
      </figcaption>

      <dialog ref={dialogRef} aria-label={title} onClose={() => setOpen(false)} onClick={event => {
    if (event.target === event.currentTarget) setOpen(false);
  }} style={{
    width: "100vw",
    height: "100vh",
    maxWidth: "100vw",
    maxHeight: "100vh",
    margin: 0,
    padding: "24px",
    border: 0,
    background: "rgba(0, 0, 0, 0.75)",
    display: open ? "flex" : "none",
    alignItems: "center",
    justifyContent: "center",
    boxSizing: "border-box"
  }}>
        {open && <div style={{
    position: "relative",
    width: "min(94vw, 1600px)",
    height: "88vh",
    borderRadius: "16px",
    overflow: "hidden",
    background: "#ffffff",
    boxShadow: "0 24px 64px rgba(0, 0, 0, 0.4)"
  }}>
            <iframe src={src} title={title} allow="clipboard-read; clipboard-write" style={{
    border: 0,
    width: "100%",
    height: "100%"
  }} />
            <button ref={closeRef} type="button" onClick={() => setOpen(false)} aria-label="Close the demo" style={{
    position: "fixed",
    top: "8px",
    right: "8px",
    width: "36px",
    height: "36px",
    borderRadius: "999px",
    border: 0,
    background: "#111827",
    color: "#ffffff",
    fontSize: "18px",
    lineHeight: "36px",
    cursor: "pointer"
  }}>
              ✕
            </button>
          </div>}
      </dialog>
    </figure>;
};

This guide shows how to turn on Borrow in an existing `@stakekit/widget` integration. If you already embed the widget for Earn, your Earn setup stays as it is. You update the widget to 0.0.290, add two config props and, if your app manages the wallet, add two methods to your wallet adapter.

Once Borrow is on, users supply collateral, borrow, manage positions, repay and withdraw collateral from the widget. To build your own Borrow interface instead, see the [Borrow API](/guides/borrow/index) guide.

<ExpandableDemo src="https://widget-pro.yield.xyz/" title="Widget Pro with Borrow" caption="Widget Pro with Borrow on." />

## At a glance

| Item | Value |
| - | - |
| Widget version | `@stakekit/widget` 0.0.290 |
| API key | The same project key you use for Earn. No extra key. |
| Borrow access | Borrow is enabled per project by the Yield.xyz team. |
| Networks in the widget | Ethereum, Base, Arbitrum and Optimism |
| Integrations | Aave V3, Morpho Blue, SparkLend and Lista Lending |
| React | React 19 if you use `SKApp` |
| Fees | Off unless configured for your project. See [Borrow monetization](/documentation/borrow/monetization). |

## Prerequisites

* **Borrow enabled for your project.** Email [hello@yield.xyz](mailto:hello@yield.xyz) with the API key you test with. Start with your dev environment.
* **A funded test wallet** on Ethereum or Base, holding collateral (for example WETH or cbBTC) and native gas. Testing on Base costs less than on Ethereum.

## Update the widget

```bash theme={null}
npm install @stakekit/widget@0.0.290
```

If you use `SKApp`, your app needs React 19. The widget now uses its own wallet dialogs instead of RainbowKit, so remove `color.connectKit` and `borderRadius.connectKit` from your theme if you set them.

<AccordionGroup>
  <Accordion title="Upgrading from an earlier version">
    Skip this section for a new integration on 0.0.290. These are the changes since 0.0.282 that can break or change an existing integration.

    **React 19.** The `react` and `react-dom` peers are `>=19`. Upgrade your host app first if it is on React 18.

    **Removed props and exports:**

    | Removed | What to do |
    | - | - |
    | `createWallet`, `evmChainGroup`, `TrackingContextProvider` | Remove the imports. Pass analytics through the `tracking` prop. |
    | `wagmi.__customConnectors__`, `mapWalletListFn` | Remove them. Use `walletPolicy` to filter and order the built-in wallet picker. |
    | `wagmi.forceWalletConnectOnly`, `forceWalletConnectOnly` | Remove them. |
    | `disableGasCheck` | Remove it. |
    | `color.connectKit`, `borderRadius.connectKit` | Remove these theme properties. |
    | `externalProviders.supportsBorrow` | Remove it. It has no effect; Borrow follows `borrowEnabled`. |
    | `tokensForEnabledYieldsOnly` | Still accepted but has no effect. Earn always lists project-enabled yields. |

    **Wallet dialogs (0.0.290).** RainbowKit is gone. The widget ships its own connect, account and chain dialogs and its own WalletConnect. Existing Tron, Cosmos and Polkadot WalletConnect sessions reconnect once after the update.

    **Adapter results (0.0.290).** `sendTransaction` can resolve `{ type: "cancelled" }` when the user declines, and `signMessage` can resolve a result object (`success`, `cancelled` or `error`) as well as a plain signature string. Borrow methods accept result objects only. `signTypedData` moved off `SKWallet` and is now a required Borrow method.

    **Browser renderer.** `renderSKWidget` returns `{ rerender, unmount }`. Call `unmount()` when your page removes the widget.

    **External provider state sync.** The widget syncs your provider snapshot (address, chain, supported chains and callbacks) on every update. Keep `externalProviders` present while the wallet is disconnected, with `currentAddress: ""`. Adding or removing `externalProviders` needs a remount.
  </Accordion>
</AccordionGroup>

## Turn on Borrow

Borrow needs two props:

* `dashboardVariant: true`. Borrow appears in the dashboard tab bar, next to Manage and Activity.
* `borrowEnabled: true`.

The widget must also group yields by category. With `dashboardVariant` on, grouping defaults to `"category"`, so remove `yieldGrouping: "flat"` if you set it.

### If the widget connects the wallet

Add the two props:

```tsx theme={null}
import "@stakekit/widget/style.css";
import { SKApp, darkTheme } from "@stakekit/widget";

export const Widget = () => (
  <SKApp apiKey={YIELD_API_KEY} theme={darkTheme} dashboardVariant borrowEnabled />
);
```

Keep your theme and other Earn props as they are. Once Borrow is enabled for your project, the Borrow tab appears next to Earn.

### If your app manages the wallet

If you pass your own wallet through `externalProviders`, your adapter needs two more methods. Your Earn methods (`signMessage`, `switchChain` and `sendTransaction`) stay as they are.

| Method | What it does |
| - | - |
| `sendBorrowTransaction` | Signs and broadcasts one Borrow transaction through your wallet. |
| `signTypedData` | Signs an EIP-712 payload when a Borrow step asks for a signature instead of a transaction. |

The widget checks for both methods when it mounts. If either is missing, it rejects the configuration with `borrow-provider-capability-missing`.

## The adapter: `SKBorrowWallet`

`SKBorrowWallet` is your Earn `SKWallet` plus the two Borrow methods. These types are exported from both `@stakekit/widget` and `@stakekit/widget/bundle`: `SKWallet`, `SKBorrowWallet`, `SKBorrowTx`, `SKBorrowTxMeta`, `SKBorrowTxResult`, `SKBorrowSignatureResult`, `SKBorrowExternalProviders` and `SKEip712TypedData`.

```tsx theme={null}
import type { SKBorrowWallet } from "@stakekit/widget";

export const wallet: SKBorrowWallet = {
  // Unchanged from your Earn adapter.
  signMessage: async (message) => yourWallet.signMessage(message),
  switchChain: async (chainId) => yourWallet.switchChain(chainId),
  sendTransaction: async (tx, txMeta) => {
    const txHash = await yourWallet.sendTransaction(tx.tx);
    return { type: "success", txHash };
  },

  // Sign an EIP-712 payload for a Borrow step.
  signTypedData: async (typedData, txMeta) => {
    const signature = await yourWallet.signTypedData(typedData);
    return { type: "success", signature };
  },

  // Sign and broadcast one Borrow transaction.
  sendBorrowTransaction: async (tx, txMeta) => {
    const txHash = await yourWallet.sendTransaction(tx.tx);
    return { type: "success", txHash };
  },
};
```

The `yourWallet` calls are placeholders for your wallet SDK or custody provider. `getTransactionReceipt` remains optional, as it is for Earn.

### Results

Both Borrow methods return a result object. A plain hash or signature string is rejected as `Invalid external provider result`.

<CodeGroup>
  ```ts sendBorrowTransaction theme={null}
  // Broadcast. Resolve as soon as it is broadcast, not after confirmation.
  { type: "success", txHash: "0x..." }

  // The user declined in the wallet. Nothing was broadcast.
  { type: "cancelled" }

  // Failed before broadcast. The user can retry the step.
  { type: "error", error: "Rejected by approver" }

  // Broadcast, then a later check failed (for example a receipt timeout).
  // The widget tracks this hash instead of asking to sign again.
  { type: "error", error: "Receipt timeout", txHash: "0x..." }
  ```

  ```ts signTypedData theme={null}
  { type: "success", signature: "0x..." }
  { type: "cancelled" }
  { type: "error", error: "Signing rejected" }
  ```
</CodeGroup>

A rejected promise counts as `error` without a `txHash`.

<Warning>
  Do not cast an Earn-only adapter to `SKBorrowWallet` to satisfy the compiler. The widget calls `sendBorrowTransaction` and `signTypedData` on it.
</Warning>

### What is in `SKBorrowTx`

`SKBorrowTx` is `{ type: "evm"; tx }`, where `tx` has:

| Field | Type | Notes |
| - | - | - |
| `to` | `0x` hex string | Target contract. |
| `from` | `0x` hex string | The connected address. |
| `data` | `0x` hex string | Calldata. |
| `value` | `0x` hex string or `undefined` | Native value, hex encoded. |
| `gas` | `0x` hex string | Gas limit from the Borrow API. |
| `chainId` | `0x` hex string | Hex encoded, not a number. |
| `type` | `"0x0"` or `"0x2"` | `"0x0"` is legacy and carries `gasPrice`. `"0x2"` is EIP-1559 and carries `maxFeePerGas` and `maxPriorityFeePerGas`. |

Fee fields are `undefined` unless the Borrow API quotes them; your signer estimates them in that case. Borrow transactions carry no nonce, so your wallet assigns it.

### What is in `SKBorrowTxMeta`

| Field | Values |
| - | - |
| `actionId` | The Borrow action id. |
| `actionType` | `"supply"`, `"borrow"`, `"repay"`, `"withdraw"`, `"enableCollateral"`, `"disableCollateral"` |
| `address` | The wallet address for the action. |
| `integrationId` | The Borrow integration (protocol) the market belongs to. |
| `txId` | The id of this transaction step. |
| `txType` | `"APPROVAL"`, `"AUTHORIZE"`, `"DEAUTHORIZE"`, `"SUPPLY"`, `"BORROW"`, `"REPAY"`, `"WITHDRAW"`, `"ENABLE_COLLATERAL"`, `"DISABLE_COLLATERAL"`, `"BUNDLE"`, `"WRAP"` |
| `rawArguments` | `marketId` (always), plus any of `amount`, `amountRaw`, `borrowAmount`, `collateralAmount`, `collateralAmountRaw`, `collateralTokenAddress`, `repayAll`, `targetLtv` and `tokenAddress` |

`actionType` is what the user asked for. `txType` is what this one transaction does. Use these fields to label the request your users or approvers see.

### How a Borrow action runs

One action can need several steps, for example an `APPROVAL`, then a `SUPPLY`, then a `BORROW`. For each step:

1. The widget asks your adapter for a transaction (`sendBorrowTransaction`) or a signature (`signTypedData`).
2. Your adapter signs, and for a transaction, broadcasts.
3. Your adapter returns the hash or the signature.
4. For a transaction, the widget reports the hash to the Borrow API and polls for confirmation: 20 checks, 2 seconds apart, about 40 seconds in total. For a signature, the widget submits it to the Borrow API itself.
5. Once the step is confirmed, the widget moves to the next one.

The widget waits for your adapter with no timeout, so custody and multi-approval flows that take several minutes still complete. Return the hash only after the transaction is broadcast. If you return it earlier, polling can end before the transaction reaches the chain.

## Pass your wallet to the widget

Keep passing your adapter through `externalProviders`, as you do for Earn.

<CodeGroup>
  ```tsx React theme={null}
  import "@stakekit/widget/style.css";
  import { SKApp, darkTheme, type SupportedSKChainIds } from "@stakekit/widget";
  import { wallet } from "./wallet";

  type Props = {
    address: string | undefined;
    chainId: SupportedSKChainIds;
    chainIds: SupportedSKChainIds[];
  };

  export const Widget = ({ address, chainId, chainIds }: Props) => (
    <SKApp
      apiKey={YIELD_API_KEY}
      theme={darkTheme}
      dashboardVariant
      borrowEnabled
      externalProviders={{
        type: "generic",
        currentAddress: address ?? "", // "" while disconnected, never null
        currentChain: chainId,
        supportedChainIds: chainIds, // include the Borrow networks you want
        provider: wallet,
      }}
    />
  );
  ```

  ```ts Browser renderer theme={null}
  import "@stakekit/widget/style.css";
  import { renderSKWidget, darkTheme, type SupportedSKChainIds } from "@stakekit/widget/bundle";
  import { wallet } from "./wallet";

  declare const initialChainId: SupportedSKChainIds; // the chain your app starts on

  const baseProps = {
    apiKey: YIELD_API_KEY,
    theme: darkTheme,
    dashboardVariant: true,
    borrowEnabled: true,
  };

  const provider = (address: string, chainId: SupportedSKChainIds) => ({
    type: "generic" as const,
    currentAddress: address,
    currentChain: chainId,
    provider: wallet,
  });

  const widget = renderSKWidget({
    container: document.getElementById("yield-widget")!,
    ...baseProps,
    externalProviders: provider("", initialChainId),
  });

  // On account or chain change:
  widget.rerender({ ...baseProps, externalProviders: provider(address, chainId) });

  // On teardown:
  widget.unmount();
  ```
</CodeGroup>

Use the exported chain id constants for `currentChain` and `supportedChainIds`, or validate the ids from your SDK, rather than casting a plain `number`.

* **Disconnected wallet.** Keep `externalProviders` and set `currentAddress: ""`. Do not pass `null` or `undefined`, and do not remove the object. To add or remove `externalProviders` itself, remount the widget.
* **Rerender.** `rerender` replaces all props. It does not merge them, so pass the full set each time.
* **Read at mount.** `apiKey`, `baseUrl`, `borrowApiUrl`, `yieldsApiUrl` and `borrowEnabled` are read when the widget mounts. Remount to change them. Other props, including `borrowMarketsScope`, update live.
* **During a flow.** Each step uses the provider you passed most recently. Keep both Borrow methods on every rerender, or the next step fails.

The widget sends your API key as the `X-API-KEY` header to `borrowApiUrl`, which defaults to `https://borrow.yield.xyz`. You do not need to set `borrowApiUrl`.

## Which markets appear

`borrowMarketsScope` sets which markets the Borrow tab lists for new borrows:

| Value | Lists |
| - | - |
| `"all"` | Default. Every supported market on the wallet's current network that passes the filters below. |
| `"enabled"` | Only the markets enabled for your project. |

```tsx theme={null}
<SKApp apiKey={YIELD_API_KEY} dashboardVariant borrowEnabled borrowMarketsScope="enabled" />
```

To use `"enabled"`, send the Yield.xyz team the markets you want for your project. Until a list is set, `"enabled"` shows no markets, so keep `"all"` until then. Existing positions stay visible and manageable under either scope.

Markets load for the wallet's current network only, on Ethereum, Base, Arbitrum and Optimism. A market is listed for new borrows only when:

* borrowing is enabled on it,
* it has at least \$10,000 of available liquidity (a loan token with no USD price counts as no liquidity),
* its borrow rate is 100% or lower, and
* it has at least one collateral with a max LTV of 1% or more that is not the loan token itself.

Lists are sorted by available liquidity. Aave V3 and SparkLend are pooled markets, labeled by loan token, where all collateral sits in one account. Morpho Blue and Lista are isolated markets, labeled by their collateral and loan token pair. The widget handles both, so no per-protocol code is needed.

## What the user sees

<Steps>
  <Step title="Borrow tab">
    The dashboard tab bar shows the Earn category tabs, then Borrow, then Manage and Activity.
  </Step>

  <Step title="Pick a market and collateral">
    The user picks a market for the wallet's network, then a collateral. Collateral the wallet holds is listed first. The user enters amounts, with a Max shortcut.
  </Step>

  <Step title="Review">
    The review shows the action, the loan-to-value and health factor before and after, and any warnings. If the Borrow API rejects the action, its reason shows here.
  </Step>

  <Step title="Steps">
    The widget runs each transaction or signature in order. Each one has its own collapsible step, with an explorer link once it is submitted.
  </Step>

  <Step title="Manage and Activity">
    Borrow positions appear under Manage next to Earn positions, where the user repays or withdraws collateral. Borrow actions appear in Activity, with a Borrow filter and a receipt showing the market, fees, loan-to-value and health factor.
  </Step>
</Steps>

**Risk checks.** The health factor limit applies to every change that adds risk. The max LTV limit blocks only changes that add debt. A withdrawal past max LTV with a health factor of 1 or more shows a warning instead. Loan-to-value turns amber from 90% of max LTV and red past it; the health factor turns amber at 1.1 or below and red below 1. A full repayment can go ahead when the wallet balance appears insufficient, because the Borrow API checks the live balance when it builds the transaction.

If you pass `tracking`, Borrow adds these page names: `"Borrow"`, `"Borrow review"`, `"Borrow steps"` and `"Borrow complete"`, and these events: `"Borrow market selected"`, `"Borrow collateral selected"`, `"Borrow page max clicked"` and `"Borrow review clicked"`.

## Testing

1. Update to 0.0.290 and turn on Borrow in your dev environment.
2. If you pass your own wallet, implement both Borrow methods.
3. Connect the test wallet on Base or Ethereum.
4. Open Borrow, pick a USDC market (for example on Aave V3 or Morpho Blue), supply collateral and borrow a small amount, such as 10 USDC.
5. Approve each step in your wallet. If you pass your own wallet, check that each request shows the expected `txType`.
6. Open Manage and check that the position shows the collateral and the debt. Check that the action shows in Activity.
7. Repay in full, then withdraw all collateral. Check that the position is gone from Manage.

If you pass your own wallet, also check that declining a request returns `{ type: "cancelled" }`, that `signTypedData` works when a step asks for it, that an empty `currentAddress` keeps the widget running, and that switching networks updates the market list. Compare the behavior with the [demo](https://widget-pro.yield.xyz/).

## Troubleshooting

The widget logs errors to the browser console. Configuration problems include an issue code and the prop path. Each entry is titled with the console message, or with the symptom when there is no message.

### Configuration

<AccordionGroup>
  <Accordion title="Initial Widget Configuration is invalid">
    After you add `borrowEnabled`, the Borrow tab does not appear or the widget does not start. The widget logs this message on first mount with one of these issues:

    | Issue | Path | Fix |
    | - | - | - |
    | `borrow-requires-dashboard` | `dashboardVariant` | Set `dashboardVariant: true`. |
    | `borrow-requires-category-grouping` | `yieldGrouping` | Remove `yieldGrouping: "flat"`. |
    | `borrow-provider-capability-missing` | `externalProviders.provider` | Implement both `sendBorrowTransaction` and `signTypedData`. |
    | `host-configuration-decode-failed` | `externalProviders.provider` | The `provider` object is missing from `externalProviders`. |
  </Accordion>

  <Accordion title="Widget Configuration update rejected">
    A rerender is ignored and the widget keeps the previous configuration. The usual causes are a partial props object (`rerender` replaces everything) or a `currentAddress` of `null` or `undefined`, which shows as `host-configuration-decode-failed`. Set `currentAddress: ""` while the wallet is disconnected.
  </Accordion>

  <Accordion title="The Borrow tab opens but shows no markets">
    Check that the wallet is on Ethereum, Base, Arbitrum or Optimism, and that the chain is in `supportedChainIds`. With `borrowMarketsScope: "enabled"`, check that markets are enabled for your project, or switch to `"all"`.
  </Accordion>
</AccordionGroup>

### Signing

<AccordionGroup>
  <Accordion title="Borrow typed-data signing capability is unavailable">
    A step needs an EIP-712 signature and the provider has no `signTypedData`. Implement it, and keep it on every rerender.
  </Accordion>

  <Accordion title="Borrow transaction capability is unavailable">
    A step started and the provider had no `sendBorrowTransaction`. Keep both Borrow methods on every provider you pass.
  </Accordion>

  <Accordion title="Invalid external provider result">
    The adapter returned something other than a result object, such as a plain hash string. Return `{ type: "success", txHash }` or `{ type: "success", signature }`.
  </Accordion>

  <Accordion title="The signed transaction payload is not available.">
    The adapter reported success without a hash. Return the hash after broadcast, or return `{ type: "error", error }`.
  </Accordion>
</AccordionGroup>

### Confirmation

<AccordionGroup>
  <Accordion title="Transaction confirmation polling was exhausted.">
    The hash came back, but the Borrow API did not see a confirmed transaction within about 40 seconds. Return the hash only after broadcast, and check that it is on the chain in `tx.tx.chainId`.
  </Accordion>

  <Accordion title="Borrow transaction ended with FAILED status.">
    Also appears as `Borrow transaction ended with NOT_FOUND status.` The transaction reverted, or the hash is wrong or on another chain. Compare the hash in your wallet with the one you returned.
  </Accordion>
</AccordionGroup>

### Wallet sync

<AccordionGroup>
  <Accordion title="Wallet Runtime invariant violated">
    `externalProviders` was added or removed after mount (`external-provider-presence-changed`). Keep the object present with `currentAddress: ""` while the wallet is disconnected, or remount the widget. Account and chain changes do not need a remount.
  </Accordion>

  <Accordion title="External provider connection failed">
    The widget tried to connect the `currentAddress` you passed and the connection failed. Check that the address and `currentChain` match the account and chain active in your wallet.
  </Accordion>
</AccordionGroup>

### Build

<AccordionGroup>
  <Accordion title="The build fails after the update">
    Look for the removed props and exports in [Upgrading from an earlier version](#update-the-widget), and check that React is on version 19.
  </Accordion>
</AccordionGroup>

## FAQ

<AccordionGroup>
  <Accordion title="Do we need a new API key for Borrow?">
    No. Borrow uses the same project key as Earn. Yield.xyz enables Borrow for that project.
  </Accordion>

  <Accordion title="Does turning on Borrow change our Earn integration?">
    No. Earn stays as it is. Borrow adds a tab and adds Borrow positions and actions to Manage and Activity. If your app manages the wallet, you add the two Borrow methods.
  </Accordion>

  <Accordion title="Can we limit the widget to certain markets?">
    Yes. Send the Yield.xyz team the markets you want for your project and set `borrowMarketsScope: "enabled"`.
  </Accordion>

  <Accordion title="Why does the widget support only four networks?">
    The widget's Borrow tab supports Ethereum, Base, Arbitrum and Optimism. The Borrow API supports more networks if you build your own UI. See [Coverage](/documentation/borrow/coverage).
  </Accordion>

  <Accordion title="Does Borrow work with custody and multi-approval wallets?">
    Yes. The widget waits for your adapter with no timeout. Return the hash once the transaction is broadcast, and the widget tracks confirmation from there.
  </Accordion>

  <Accordion title="Are there fees?">
    Not unless they are configured for your project. Fees apply to Morpho Blue markets. Contact [hello@yield.xyz](mailto:hello@yield.xyz) and see [Borrow monetization](/documentation/borrow/monetization).
  </Accordion>
</AccordionGroup>

## Next steps

<Columns cols={2}>
  <Card title="Borrow API" icon="code" href="/guides/borrow/index">
    The full lifecycle behind the widget, for teams that build their own UI.
  </Card>

  <Card title="Core concepts" icon="shapes" href="/documentation/borrow/core-concepts/index">
    Markets, positions, health factor and the action lifecycle.
  </Card>
</Columns>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.