Skip to content

Button

In order to render buttons you should use our <Button> component, button() helper or plain objects. Frames.js supports the following button types:

Post Button

import { Button, button } from "frames.js/core";
 
<Button action="post">Click me</Button>;
 
button({ action: "post", label: "Click me" });
 
// plain object
{
  action: "post",
  label: "Click me",
}

This button sends a request to a URL on which it was rendered. If you want to navigate to different route in your app, you can use target prop which should be set to relative path.

<Button action="post" target="/next-route">
  Click me
</Button>;
 
button({ action: "post", target: "/next-route", label: "Click me" });
 
// plain object
{
  action: "post",
  target: "/next-route",
  label: "Click me",
}

The target path will be resolved relatively to current URL.

Passing state between frames

<Button
  action="post"
  target={{ query: { foo: "bar" }, pathname: "/next-route" }}
>
  Click me
</Button>;
 
button({
  action: "post",
  target: { query: { foo: "bar" }, pathname: "/next-route" },
  label: "Click me",
});
 
// plain object
{
  action: "post",
  target: { query: { foo: "bar" }, pathname: "/next-route" },
  label: "Click me",
}

The state will be available in the frame handler via ctx.searchParams e.g.

ctx.searchParams.foo; // bar

Post Redirect Button

Post redirect button is a special kind of post button that when users clicks the button, first a message is sent to frames handler and then it is expected that handler will redirect the user to a different route using redirect call.

It accepts same props as post button.

import { Button, button } from "frames.js/core";
 
<Button
  action="post_redirect"
  target={{ query: { foo: "bar" }, pathname: "/next-route" }}
>
  Click me
</Button>;
 
button({
  action: "post_redirect",
  target: { query: { foo: "bar" }, pathname: "/next-route" },
  label: "Click me",
});
 
// plain object
{
  action: "post_redirect",
  target: { query: { foo: "bar" }, pathname: "/next-route" },
  label: "Click me",
}

Link Button

Link button navigates to external URL when clicked.

import { Button, button } from "frames.js/core";
 
<Button action="link" target="https://google.com">
  Click me
</Button>;
 
button({ action: "link", target: "https://google.com", label: "Click me" });
 
// plain object
{
  action: "link",
  target: "https://google.com",
  label: "Click me",
}

Mint Button

Mint button creates a new token when clicked.

import { Button, button } from "frames.js/core";
import { getTokenUrl } from "frames.js";
 
<Button
  action="mint"
  target={getTokenUrl({
    address: "0x8f5ed2503b71e8492badd21d5aaef75d65ac0042",
    chain: zora,
    tokenId: "3",
  })}
>
  Mint
</Button>;
 
button({
  action: "mint",
  target: getTokenUrl({
    address: "0x8f5ed2503b71e8492badd21d5aaef75d65ac0042",
    chain: zora,
    tokenId: "3",
  }),
  label: "Mint",
});
 
// plain object
{
  action: "mint",
  target: getTokenUrl({
    address: "0x8f5ed2503b71e8492badd21d5aaef75d65ac0042",
    chain: zora,
    tokenId: "3",
  }),
  label: "Mint",
}

Tx Button

Tx button requests transaction or signature data from target when clicked, and sends the transaction ID or signature to the frame's post_url or the button's post_url if it is set.

import { Button, button } from "frames.js/core";
 
<Button action="tx" target="/txdata" post_url="/tx-success">
  Buy storage
</Button>;
 
button({
  action: "tx",
  target: "/txdata",
  post_url: "/tx-success",
  label: "Buy storage",
});
 
// plain object
{
  action: "tx",
  target: "/txdata",
  post_url: "/tx-success",
  label: "Buy storage",
}

Transaction data

The endpoint specified in target should return a JSON object with the following structure:

type TransactionTargetResponse =
  | TransactionTargetResponseSendTransaction
  | TransactionTargetResponseSignTypedDataV4;
 
type EthSendTransactionParams = {
  /** JSON ABI. This must include the encoded function type and should include any potential error types. */
  abi: JSON | Abi | [];
  /** transaction to address */
  to: Address;
  /** value of ether to send with the transaction in wei */
  value?: string;
  /** optional transaction call data */
  data?: Hex;
};
 
type TransactionTargetResponseSendTransaction = {
  /** A [CAIP-2](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md) chain ID to identify the tx network e.g. 'eip155:1' for Ethereum mainnet */
  chainId: string;
  method: "eth_sendTransaction";
  /** Specific parameters for chainId and method */
  params: EthSendTransactionParams;
  /** Return false to omit the [calldata attribution](https://www.notion.so/warpcast/Frame-Transactions-Public-9d9f9f4f527249519a41bd8d16165f73#c1c3182208ce4ae4a7ffa72129b9795a) suffix. If this value is undefined or true, clients will append the attribution suffix. */
  attribution?: boolean;
};
 
type EthSignTypedDataV4Params = {
  domain: {
    chainId?: number;
    name?: string;
    salt?: Hex;
    verifyingContract?: Address;
    version?: string;
  };
  types: TypedData;
  primaryType: string;
  message: Record<string, unknown>;
};
 
type TransactionTargetResponseSignTypedDataV4 = {
  /** A [CAIP-2](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md) chain ID to identify the tx network e.g. 'eip155:1' for Ethereum mainnet */
  chainId: string;
  method: "eth_signTypedData_v4";
  /** Specific parameters for chainId and method */
  params: EthSignTypedDataV4Params;
};

Transaction Success

If the transaction or signing is successful, the frame will send a POST request to the URL specified in post_url or the frame post_url. This can be handled by the frame to show a success message or redirect the user to a different page.

route.tsx
import { Button } from "frames.js/next";
import { createFrames } from "frames.js/next";
 
const export frames = createFrames()
 
export const GET = frames(async (ctx) => {
  return {
    image: (
      <div tw="flex">
        Execute transaction
      </div>
    ),
    buttons: [
      <Button action="tx" target="/txdata" post_url="/tx-success">
        Execute
      </Button>,
      // or using button helper
      button({
        action: "tx",
        target: "/txdata",
        post_url: "/tx-success",
        label: "Execute",
      }),
    ],
  };
})
// /tx-success/route.tsx
 
import { frames } from ../route.tsx";
 
const handleRequest = frames(async (ctx) => {
  if (message?.transactionId) {
    return {
      image: (
        <div tw="flex">
          Transaction submitted! {ctx.message.transactionId}
        </div>
      ),
      buttons: [
        <Button
          action="link"
          target={`https://www.onceupon.gg/tx/${ctx.message.transactionId}`}
        >
          View on block explorer
        </Button>,
        // or using button helper
        button({
          action: "link",
          target: `https://www.onceupon.gg/tx/${ctx.message.transactionId}`,
          label: "View on block explorer",
        }),
      ],
    };
  }
});