frames.js is a TypeScript library and framework for writing and testing Farcaster Frames.
- ⚡️ Local frames debugger
- 🥳 Write Frames using React
- 🔋 Batteries included framework
- 🌴 Tree-shakeable & Lightweight
- 🚀 Library with all the functions
Look at our documentation to learn more about frames.js.
npm install frames.jsyarn add frames.jsRun to clone the starter into a new folder called framesjs-starter
npx degit github:framesjs/frames.js/examples/framesjs-starter#main framesjs-starteryarn add frames.jsCreate a frames directory in your Next.js app and add the following files:
import{createFrames}from"frames.js/next";exportconstframes=createFrames({basePath: "/frames",});/* eslint-disable react/jsx-key */import{Button}from"frames.js/next";import{frames}from"./frames";consthandleRequest=frames(async(ctx)=>{return{image: (<span>{ctx.pressedButton
? `I clicked ${ctx.searchParams.value}`
: `Click some button`}</span>),buttons: [<Buttonaction="post"target={{query: {value: "Yes"}}}>
Say Yes
</Button>,<Buttonaction="post"target={{query: {value: "No"}}}>
Say No
</Button>,],};});exportconstGET=handleRequest;exportconstPOST=handleRequest;import{fetchMetadata}from"frames.js/next";exportasyncfunctiongenerateMetadata(){return{title: "My Page",// ...other: {// ...
...(awaitfetchMetadata(// provide a full URL to your /frames endpointnewURL("/frames",process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: "http://localhost:3000"))),},};}exportdefaultfunctionPage(){return<span>My existing page</span>;}// page that renders a frame// ./app/page.tsximport{Frame,getFrameFlattened}from"frames.js";importtype{Metadata}from"next";// Declare the frameconstinitialFrame: Frame={image: "https://picsum.photos/seed/frames.js/1146/600",version: "vNext",buttons: [{label: "Random image",},],postUrl: `${process.env.NEXT_PUBLIC_HOST}/frames`,};// Export Next.js metadataexportconstmetadata: Metadata={title: "Random Image Frame",description: "This is an example of a simple frame using frames.js",openGraph: {images: [{url: "https://picsum.photos/seed/frames.js/600",},],},other: getFrameFlattened(initialFrame),};// handle frame actions// ./app/frames/route.tsimport{getFrameHtml,validateFrameMessage}from"frames.js";import{NextRequest}from"next/server";exportasyncfunctionPOST(request: NextRequest){constbody=awaitrequest.json();// Parse and validate the frame messageconst{ isValid, message }=awaitvalidateFrameMessage(body);if(!isValid||!message){returnnewResponse("Invalid message",{status: 400});}constrandomInt=Math.floor(Math.random()*100);constimageUrlBase=`https://picsum.photos/seed/${randomInt}`;// Use the frame message to build the frameconstframe={version: "vNext",image: `${imageUrlBase}/1146/600`,buttons: [{label: `Next (pressed by ${message.data.fid})`,},],ogImage: `${imageUrlBase}/600`,postUrl: `${process.env.NEXT_PUBLIC_HOST}/frames`,};// Return the frame as HTMLconsthtml=getFrameHtml(frame);returnnewResponse(html,{headers: {"Content-Type": "text/html",},status: 200,});}Distributed under an MIT License. See LICENSE for more information.
Check out the following places for more Frames-related content:
- Join the /frames-dev channel on Farcaster to ask questions
- Follow Frames.js & team (@df and @stephancill) on Farcaster for updates
- Star frames.js on GitHub to show your support and keep track of updates
- Browse the awesome-frames list of awesome Frames projects and resources

