Skip to content

createFrames

createFrames() creates a function which accepts your frames handler and returns a request handler.

Usually you shouldn't use this function directly but rather use one of our adapters based on a framework you are using, e.g. Express.js, Hono, Next.js or Remix.

import { createFrames } from "frames.js/core";
 
const frames = createFrames();
const handleRequest = frames(async (ctx) => {
  return {
    image: <span>Test</span>,
  };
});

The function passed to frames will be called with the context of a frame action and should return a FrameDefinition.

Parameters

createFrames accepts an optional options object with the following properties:

basePath

  • Type: string

A string that specifies the base path for all relative URLs in the frame definition. It defaults to /. If baseUrl is provided, it will be resolved relatively to baseUrl. If the baseUrl option is not provided, it will use URL of current request and override its path with basePath when generating target URLs.

baseUrl

  • Type: string | URL

A string or URL object that specifies the base URL for all relative URLs in the frame definition. Can be used to override the URL detected from the request.

initialState

  • Type: generic

A JSON serializable value that is used if no state is provided in the message or you are on the initial frame.

middleware

See the Middleware guide for more information.

Type: FramesMiddleware

An array of middleware functions that are called before the frame handler and allows you to inject additional context into the ctx parameter passed to each frame handler call.

Each middleware should return a promise that resolves to the next middleware, or a Web API Response, or a FrameDefinition.

Types

For strong type support in the handler, the middleware should be typed as FramesMiddleware<any, YourContextType>.

import { createFrames, types } from "frames.js/next";
 
const myMiddleware: types.FramesMiddleware<any, { foo?: string }> = async (
  ctx,
  next
) => {
  return next({ foo: "bar" });
};

Example

./app/frames/route.tsx
const frames = createFrames({
  middleware: [
    async (ctx, next) => {
      console.log("Before frame handler");
      const result = await next({ name: "Alice" });
      console.log("After frame handler");
      return result;
    },
  ],
});
 
const handler = frames(async (ctx) => {
  return {
    image: <span>{ctx.name}</span>, // Outputs an image with the text "Alice"
  };
});

FrameDefinition

FrameDefinition is an object that describes a frame. It has the following properties:

image

  • Type: React.ReactElement | string

The image to be rendered in the frame. If a string is provided, it must be a valid URL.

imageOptions

  • Type: { aspectRatio?: "1.91:1" | "1:1" } & ConstructorParameters<typeof ImageResponse>[1]

Options for the image. The aspectRatio property can be set to "1.91:1" or "1:1".

buttons

  • Type: 1, 2, 3, or 4 FrameButtonElement elements

An array of buttons to be rendered in the frame. The buttons are rendered in the order they are provided.

Example

import { Button } from "frames.js/next";
 
const handleRequest = frames(async (ctx) => {
  return {
    image: <span>Test</span>,
    buttons: [
      <Button action="post">Post</Button>,
      <Button action="post_redirect">Post Redirect</Button>,
    ],
  };
});

textInput

  • Type: string

Label for text input. If no value is provided, the input is not rendered.

state

  • Type: JsonValue

Global app state that will be available on the next frame.

headers

  • Type: HeadersInit

Custom headers to be included in the response.

The ResponseInit properties allow you to specify custom headers such as Cache-Control.

Cache-Control

If you wish to have dynamic content in your frame which is re-rendered on every feed reload, you can specify a low Cache-Control max-age value.

Here is an example of a frame definition with a Cache-Control header set to max-age=0. It will always show the current time in the user's feed.

/* eslint-disable react/jsx-key */
import { Button } from "frames.js/next";
import { createFrames } from "frames.js/next";
 
const frames = createFrames();
 
const handleRequest = frames(async (ctx) => {
  return {
    image: (
      <div tw="bg-purple-800 text-white w-full h-full justify-center items-center flex">
        The current time is {new Date().toLocaleString()}
      </div>
    ),
    imageOptions: {
      aspectRatio: "1:1",
    },
    buttons: [<Button action="post">Refresh</Button>],
    headers: { 
      // Max cache age in seconds
      "Cache-Control": "max-age=0", 
    }, 
  };
});

Request Handler

The resulting handleRequest is a function that accepts Web API Request and returns a Promise that resolves to Web API Response.

handleRequest(new Request("/")).then((res) => {
  return res.text();
});

Per-route middleware

You can also pass middleware to the handleRequest function to be executed only for that specific route.

const handleRequest = frames(
  async (ctx) => {
    return {
      image: <span>Test</span>,
    };
  },
  {
    middleware: [farcasterHubContext({ hubHttpUrl: process.env.HUB_HTTP_URL })],
  }
);

This will only execute the farcasterHubContext middleware for the route that handleRequest is called with.

Context

Core middleware is included and executed by default and gives you access to the following default context in your frame handlers:

basePath

  • Type: string

Specifies the base path for all relative URLs in the frame definition.

baseUrl

  • Type: URL

The resolved base URL for all relative URLs in the frame definition. All relative URLs are resolved relatively to this value.

initialState

  • Type: generic

A JSON serializable value that is used if no state is provided in the message or you are on the initial frame.

request

The request object that was passed to the request handler.

url

The URL object that was parsed from the request.

searchParams

  • Type: Record<string, string>

The search params in the URL as an object. If there are no search params, it will be an empty object.

pressedButton

  • Type: undefined | { action: "post" | "post_redirect"; index: 1 | 2 | 3 | 4 }

The button that was clicked on the previous frame.

buttonIndex

  • Type: number

The index of the button that was clicked on the previous frame.

message

  • Type: FrameMessage | undefined

The frame message that was parsed from the request body.

clientProtocol

  • Type: ClientProtocolId | undefined

The client protocol that was used to send the frame message.

state

  • Type: JsonValue

The state extracted from the frame message. If you are on the initial frame (no button pressed), the value is the initialState value passed to createFrames. If you are on a frame with a button pressed, the value is the state from the previous frame.