frames.js
frames.js is a TypeScript library and framework for writing and testing Farcaster Frames.
Features
- ⚡️ Local frames debugger
- 🥳 Write Frames using React
- 🔋 Batteries included framework
- 🌴 Tree-shakeable & Lightweight
- 🚀 Library with all the functions
Documentation
Look at our documentation to learn more about frames.js.
Installation
npm install frames.js
yarn add frames.js
Quick Start
Clone the frames.js starter template (with local debugger)
Run to clone the starter into a new folder called framesjs-starter
npx degit github:framesjs/frames.js/examples/framesjs-starter
or clone from github
Alternatively, add frames.js to your existing project manually
1. Add frames.js
to your project
yarn add frames.js
2. Create your Frames app
Create a frames
directory in your Next.js app and add the following files:
./app/frames/frames.ts
import { createFrames } from "frames.js/next";
export const frames = createFrames({
basePath: "/frames",
});
3. Create a Frames route
./app/frames/route.tsx
import { Button } from "frames.js/next";
import { frames } from "./frames";
const handleRequest = frames(async (ctx) => {
return {
image: (
<span>
{ctx.pressedButton
? `I clicked ${ctx.searchParams.value}`
: `Click some button`}
</span>
),
buttons: [
<Button action="post" target={{ query: { value: "Yes" } }}>
Say Yes
</Button>,
<Button action="post" target={{ query: { value: "No" } }}>
Say No
</Button>,
],
};
});
export const GET = handleRequest;
export const POST = handleRequest;
4. Include Frames alongside your existing page's metadata
import { fetchMetadata } from "frames.js/next";
export async function generateMetadata() {
return {
title: "My Page",
other: {
...(await fetchMetadata(
new URL(
"/frames",
process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: "http://localhost:3000"
)
)),
},
};
}
export default function Page() {
return <span>My existing page</span>;
}
5. Done! 🎉
Debugging your Frames locally
Prefer not to use a Framework?
You can use frames.js library helper functions instead
import { Frame, getFrameFlattened } from "frames.js";
import type { Metadata } from "next";
const initialFrame: Frame = {
image: "https://picsum.photos/seed/frames.js/1146/600",
version: "vNext",
buttons: [
{
label: "Random image",
},
],
postUrl: `${process.env.NEXT_PUBLIC_HOST}/frames`,
};
export const metadata: 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),
};
import { getFrameHtml, validateFrameMessage } from "frames.js";
import { NextRequest } from "next/server";
export async function POST(request: NextRequest) {
const body = await request.json();
const { isValid, message } = await validateFrameMessage(body);
if (!isValid || !message) {
return new Response("Invalid message", { status: 400 });
}
const randomInt = Math.floor(Math.random() * 100);
const imageUrlBase = `https://picsum.photos/seed/${randomInt}`;
const frame = {
version: "vNext",
image: `${imageUrlBase}/1146/600`,
buttons: [
{
label: `Next (pressed by ${message.data.fid})`,
},
],
ogImage: `${imageUrlBase}/600`,
postUrl: `${process.env.NEXT_PUBLIC_HOST}/frames`,
};
const html = getFrameHtml(frame);
return new Response(html, {
headers: {
"Content-Type": "text/html",
},
status: 200,
});
}
License
Distributed under an MIT License. See LICENSE for more information.
Check out the following places for more Frames-related content: