base on Farcaster Frames in less than 100 lines, and ready to be deployed to Vercel. <p align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://github.com/Zizzamia/a-frame-in-100-lines/blob/main/public/park-4.png"> <img alt="OnchainKit logo vibes" src="https://github.com/Zizzamia/a-frame-in-100-lines/blob/main/public/park-4.png" width="auto"> </picture> </p> # A Frame in 100 lines (or less) Farcaster Frames in less than 100 lines, and ready to be deployed to Vercel. To test a **deployed** Frame, use: https://warpcast.com/~/developers/frames. To test a **localhost** Frame, use: [Framegear](https://onchainkit.xyz/frame/framegear). A simple tool that allows you to run and test your frames locally: - without publishing - without casting - without spending warps And let us know what you build by either mentioning @zizzamia on [Warpcast](https://warpcast.com/zizzamia) or [X](https://twitter.com/Zizzamia). <br /> Have fun! ⛵️ <br /> ## App Routing files - app/ - [config.ts](https://github.com/Zizzamia/a-frame-in-100-lines?tab=readme-ov-file#appconfigts) - [layout.tsx](https://github.com/Zizzamia/a-frame-in-100-lines?tab=readme-ov-file#applayouttsx) - [page.tsx](https://github.com/Zizzamia/a-frame-in-100-lines?tab=readme-ov-file#apppagetsx) - api/ - frame/ - [route.ts](https://github.com/Zizzamia/a-frame-in-100-lines?tab=readme-ov-file#appapiframeroutets) <br /> ### `app/page.tsx` ```tsx import { getFrameMetadata } from '@coinbase/onchainkit/frame'; import type { Metadata } from 'next'; import { NEXT_PUBLIC_URL } from './config'; const frameMetadata = getFrameMetadata({ buttons: [ { label: 'Story time!', }, { action: 'link', label: 'Link to Google', target: 'https://www.google.com', }, { label: 'Redirect to pictures', action: 'post_redirect', }, ], image: { src: `${NEXT_PUBLIC_URL}/park-3.png`, aspectRatio: '1:1', }, input: { text: 'Tell me a boat story', }, postUrl: `${NEXT_PUBLIC_URL}/api/frame`, }); export const metadata: Metadata = { title: 'zizzamia.xyz', description: 'LFG', openGraph: { title: 'zizzamia.xyz', description: 'LFG', images: [`${NEXT_PUBLIC_URL}/park-1.png`], }, other: { ...frameMetadata, }, }; export default function Page() { return ( <> <h1>zizzamia.xyz</h1> </> ); } ``` ### `app/layout.tsx` ```tsx export const viewport = { width: 'device-width', initialScale: 1.0, }; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body>{children}</body> </html> ); } ``` ### `app/config.ts` ```ts export const NEXT_PUBLIC_URL = 'https://zizzamia.xyz'; ``` ### `app/api/frame/route.ts` ```ts import { FrameRequest, getFrameMessage, getFrameHtmlResponse } from '@coinbase/onchainkit/frame'; import { NextRequest, NextResponse } from 'next/server'; import { NEXT_PUBLIC_URL } from '../../config'; async function getResponse(req: NextRequest): Promise<NextResponse> { let accountAddress: string | undefined = ''; let text: string | undefined = ''; const body: FrameRequest = await req.json(); const { isValid, message } = await getFrameMessage(body, { neynarApiKey: 'NEYNAR_ONCHAIN_KIT' }); if (isValid) { accountAddress = message.interactor.verified_accounts[0]; } if (message?.input) { text = message.input; } if (message?.button === 3) { return NextResponse.redirect( 'https://www.google.com/search?q=cute+dog+pictures&tbm=isch&source=lnms', { status: 302 }, ); } return new NextResponse( getFrameHtmlResponse({ buttons: [ { label: `🌲 ${text} 🌲`, }, ], image: { src: `${NEXT_PUBLIC_URL}/park-1.png`, }, postUrl: `${NEXT_PUBLIC_URL}/api/frame`, }), ); } export async function POST(req: NextRequest): Promise<Response> { return getResponse(req); } export const dynamic = 'force-dynamic'; ``` <br /> ## Resources - [Official Farcaster Frames documentation](https://docs.farcaster.xyz/learn/what-is-farcaster/frames) - [Official Farcaster Frame specification](https://docs.farcaster.xyz/reference/frames/spec) - [OnchainKit documentation](https://onchainkit.xyz) <br /> ## Community ☁️ 🌁 ☁️ Check out the following places for more OnchainKit-related content: - Follow @zizzamia ([X](https://twitter.com/zizzamia), [Farcaster](https://warpcast.com/zizzamia)) for project updates - Join the discussions on our [OnchainKit warpcast channel](https://warpcast.com/~/channel/onchainkit) ## Authors - [@zizzamia](https://github.com/zizzamia.png) ([X](https://twitter.com/Zizzamia)) - [@cnasc](https://github.com/cnasc.png) ([warpcast](https://warpcast.com/cnasc)) ## License This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details ", Assign "at most 3 tags" to the expected json: {"id":"7366","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"