Skip to content

Get started with Open Frames for apps built with XMTP

You can support Frames in apps built with XMTP by using the Open Frames standard. This standard enables you to build a Frame that works in multiple ecosystems, including Farcaster, XMTP, Lens, and others.

This tutorial describes how to display basic, transactional, and subscription Open Frames in an app built with XMTP.

XMTP apps that support Open Frames

These apps built with XMTP can display Open Frames:

Display basic Open Frames

Use this library to display basic Opens Frames in your app built with XMTP: @xmtp/frames-client

Validate POST payloads from Open Frames

Use these tools to validate create and sign POST payloads from Open Frames used with XMTP: @xmtp/frames-validator

Get started with supporting frameworks

OnchainKit

OnchainKit supports XMTP payloads.

Frame metadata

To build an Open Frame for XMTP, you must first add XMTP metadata:

TypeScript
const frameMetadata = getFrameMetadata({
  /**
   * Frame metadata like Image, Buttons, Input, etc.
   */
  isOpenFrame: true,
  accepts: { xmtp: "vNext" },
});
 
export const metadata: Metadata = {
  /**
   * ...other metadata
   */
  other: {
    ...frameMetadata,
  },
};

How to validate incoming messages

TypeScript
import {
  isXmtpFrameRequest,
  getXmtpFrameMessage,
} from "@coinbase/onchainkit/xmtp";
/* ... */
async function getResponse(req: any): Promise<NextResponse> {
  const body: FrameRequest = await req.json();
  if (isXmtpFrameRequest(body)) {
    const { isValid, message } = await getXmtpFrameMessage(body);
    // ... do something with the message if isValid is true
    if (isValid) {
      const { verifiedWalletAddress } = message;
      // ... do something with the verifiedWalletAddress
    }
  } else {
    // ...
  }
}

Frames.js

Frames.js supports XMTP payloads.

Frame metadata

To build an Open Frame for XMTP, you must first add XMTP metadata:

TypeScript
const acceptedProtocols: ClientProtocolId[] = [
  {
    id: "xmtp",
    version: "vNext",
  },
  {
    id: "farcaster",
    version: "vNext",
  },
];

How to validate incoming messages

TypeScript
import { getXmtpFrameMessage, isXmtpFrameActionPayload } from "frames.js/xmtp";
 
let fid: number | undefined;
let walletAddress: string | undefined;
 
if (isXmtpFrameActionPayload(previousFrame.postBody)) {
  const frameMessage = await getXmtpFrameMessage(previousFrame.postBody);
  const { verifiedWalletAddress } = frameMessage;
  // Do something with xmtp wallet address
} else {
  // Do something else
}

Frog

Frame metadata

To build a Frame with XMTP, you must first add XMTP metadata.

TypeScript
const addMetaTags = (client: string, version?: string) => {
  // Follow the OpenFrames meta tags spec
  return {
    unstable_metaTags: [
      { property: `of:accepts`, content: version || "vNext" },
      { property: `of:accepts:${client}`, content: version || "vNext" },
    ],
  };
};
 
export const app = new Frog(addMetaTags("xmtp"));

How to validate incoming messages

To validate incoming messages, install the @xmtp/frames-validator package:

npm install @xmtp/frames-validator

Then add the middleware:

TypeScript
import { validateFramesPost } from "@xmtp/frames-validator";
 
const xmtpSupport = async (c: Context, next: Next) => {
  // Check if the request is a POST and relevant for XMTP processing
  if (c.req.method === "POST") {
    const requestBody = (await c.req.json().catch(() => {})) || {};
    if (requestBody?.clientProtocol?.includes("xmtp")) {
      c.set("client", "xmtp");
      const { verifiedWalletAddress } = await validateFramesPost(requestBody);
      c.set("verifiedWalletAddress", verifiedWalletAddress);
    } else {
      // Add farcaster check
      c.set("client", "farcaster");
    }
  }
  await next();
};
 
app.use(xmtpSupport);

Access a verified wallet address

TypeScript
app.frame("/", (c) => {
  /* Get Frame variables */
  const { buttonValue, inputText, status } = c;
 
  // XMTP verified address
  const { verifiedWalletAddress } = c?.var || {};
 
  /* return */
});