This tutorial walks you through a the code for a Warpcast frame that implements a Buzzfeed-style quiz using Coinbase's OnchainKit, dynamic Open Graph (OG) images, and robust API routes.
The code for the frame can be found in this github repository
Prerequisites
Familiarity with Next.js and React.
Basic understanding of APIs and server-side rendering.
Installed dependencies:
@coinbase/onchainkit
sharp
Step 1: Page Setup (/app/page.tsx
)
The app starts with a simple page containing metadata and a title.
Key Features:
Dynamic Metadata: Uses
getFrameMetadata
from OnchainKit to set up Open Graph data for sharing and buttons for user interaction.Page Content: Displays a heading with the quiz title.
import { getFrameMetadata } from '@coinbase/onchainkit/frame';
import type { Metadata } from 'next';
import { NEXT_PUBLIC_URL } from './config';
const frameMetadata = getFrameMetadata({
buttons: [{ label: 'Start Quiz' }],
image: { src: `${NEXT_PUBLIC_URL}/image.jpg`, aspectRatio: '1:1' },
postUrl: `${NEXT_PUBLIC_URL}/api/frame`,
});
export const metadata: Metadata = {
title: 'Project Buidl On Base Buzzfeed Quiz Frame',
description: 'LFG',
openGraph: { title: 'Project Buidl On Base Buzzfeed Quiz Frame', images: [`${NEXT_PUBLIC_URL}/image.jpg`] },
other: { ...frameMetadata },
};
export default function Page() {
return (
<>
<h1>Project Buidl On Base Buzzfeed Quiz Frame</h1>
</>
);
}
Step 2: Layout (/app/layout.tsx
)
Defines the root layout for the app, including viewport settings and a container for child components.
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>
);
}
Step 3: API for Quiz Logic (/app/api/frame/route.ts
)
Handles the core quiz logic through a POST request. It processes user input, tracks quiz state, and serves dynamic responses.
Key Features:
State Management:
- Tracks the
questionIndex
andanswers
viauntrustedData
.
- Tracks the
Dynamic Responses:
If the quiz is complete, generates a results frame.
Otherwise, serves the next question with dynamically generated options.
Error Handling:
- Returns a "Try Again" button for errors.
import { FrameRequest, getFrameHtmlResponse } from '@coinbase/onchainkit/frame';
import { NextRequest, NextResponse } from 'next/server';
const questions = [...]; // Array of quiz questions and options.
async function getResponse(req: NextRequest): Promise<NextResponse> {
// Logic to handle quiz progress and generate frames
}
export async function POST(req: NextRequest): Promise<Response> {
return getResponse(req);
}
export const dynamic = 'force-dynamic';
Step 4: Open Graph for Quiz Questions (/api/og/question/route.ts
)
Generates a dynamic image for each quiz question.
Key Features:
Dynamic Text Rendering:
- Overlays the question text on a background image using
sharp
.
- Overlays the question text on a background image using
Error Handling:
- Returns a
500
status and logs errors if image generation fails.
- Returns a
import sharp from 'sharp';
export async function GET(req: NextRequest): Promise<NextResponse> {
// Parse question index and generate image using sharp.
}
Step 5: Open Graph for Quiz Results (/api/og/results/route.ts
)
Generates an OG image displaying the user's quiz result.
Key Features:
Result Calculation:
- Calculates a stablecoin personality based on the answers.
Dynamic Image Rendering:
- Uses
sharp
to overlay the result text on a background image.
- Uses
function calculateResult(answers: number[]): string {
// Logic to determine the quiz result.
}
export async function GET(req: NextRequest): Promise<NextResponse> {
// Parse answers, calculate result, and generate image using sharp.
}
Step 6: Error Handling (/api/og/error/route.ts
)
Handles errors by serving a static error image.
Key Features:
Edge Runtime:
- Uses the
edge
runtime for optimized performance.
- Uses the
Simple JSX Rendering:
- Renders a friendly error message as an Open Graph image.
import { ImageResponse } from 'next/og';
export const runtime = 'edge';
export async function GET(req: NextRequest) {
return new ImageResponse(
(
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
<h1>Something went wrong!</h1>
<p>Please try again</p>
</div>
),
{ width: 1200, height: 630 }
);
}
To test the frame, go the web version of Warpcast on your computer and navigate to this link
https://warpcast.com/~/developers/frames-legacy
Using this tutorial, you should be able to create your own Buzzfeed style quiz frame!