Next.js
Next.js is a React framework for building full-stack web applications.
Quick Start
You can scaffold a Frog project with Next.js integrated via the create-frog CLI:
npm init frog -- -t nextManual Installation
Install Next.js
npm install next@latest react@latest react-dom@latest vercel@latestBuild your Frame
Next, scaffold your frame:
/** @jsxImportSource frog/jsx */
import { Button, Frog, TextInput } from 'frog'
const app = new Frog({
basePath: '/api',
title: 'Frog Frame',
})
app.frame('/', (c) => {
const { buttonValue, status } = c
return c.res({
image: (
<div style={{ color: 'white', display: 'flex', fontSize: 60 }}>
{status === 'initial' ? (
'Select your fruit!'
) : (
`Selected: ${buttonValue}`
)}
</div>
),
intents: [
<Button value="apple">Apple</Button>,
<Button value="banana">Banana</Button>,
<Button value="mango">Mango</Button>
]
})
})Add Handlers
After that, we will add handlers to handle requests in the api/ route:
/** @jsxImportSource frog/jsx */
import { Button, Frog, TextInput } from 'frog'
import { handle } from 'frog/next'
const app = new Frog({
basePath: '/api',
title: 'Frog Frame',
})
// Uncomment to use Edge Runtime
// export const runtime = 'edge'
app.frame('/', (c) => {
const { buttonValue, status } = c
return c.res({
image: (
<div style={{ color: 'white', display: 'flex', fontSize: 60 }}>
{status === 'initial' ? (
'Select your fruit!'
) : (
`Selected: ${buttonValue}`
)}
</div>
),
intents: [
<Button value="apple">Apple</Button>,
<Button value="banana">Banana</Button>,
<Button value="mango">Mango</Button>
]
})
})
export const GET = handle(app)
export const POST = handle(app)Setup Devtools
Add Frog Devtools after all frames are defined. This way the devtools can automatically discover all your frames.
import { Button, Frog } from 'frog'
import { handle } from 'frog/next'
import { devtools } from 'frog/dev'
import { serveStatic } from 'frog/serve-static'
const app = new Frog({
basePath: '/api',
title: 'Frog Frame',
})
app.frame('/', (c) => {
...
})
devtools(app, { serveStatic })
^ Devtools should be called after all frames are defined. export const GET = handle(app)
export const POST = handle(app)Add Scripts to package.json
Then we will add a Wrangler scripts to our package.json:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"deploy": "vercel",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"hono": "latest",
"frog": "latest",
"next": "latest",
"react": "latest",
"react-dom": "latest",
"vercel": "latest",
},
}
Navigate to Frame
Then, we can navigate to our frame in the browser:
npm run dev
http://localhost:3000/api
Bonus: Deploy
When ready, we can deploy our application.
This example deploys to Vercel via the Vercel CLI (vercel), but you can use any platform that supports Next.js
npm run deployBonus: Add Browser Redirects
If a user navigates to your frame in the browser, we may want to redirect them to the correct Next.js page route that corresponds to the frame.
In the example below, when a user navigates to the /api/foo path of the website via their web browser,
they will be redirected to the /foo path (ie. /app/foo/page.tsx in your Next.js App).
Read more on Browser Redirects
/** @jsxImportSource frog/jsx */
import { Button, Frog, TextInput } from 'frog'
import { handle } from 'frog/next'
const app = new Frog({
basePath: '/api',
browserLocation: '/:path',
title: 'Frog Frame',
})
// Uncomment to use Edge Runtime
// export const runtime = 'edge'
app.frame('/foo', (c) => {
const { buttonValue, status } = c
return c.res({
image: (
<div style={{ color: 'white', display: 'flex', fontSize: 60 }}>
{status === 'initial' ? (
'Select your fruit!'
) : (
`Selected: ${buttonValue}`
)}
</div>
),
intents: [
<Button value="apple">Apple</Button>,
<Button value="banana">Banana</Button>,
<Button value="mango">Mango</Button>
]
})
})
export const GET = handle(app)
export const POST = handle(app) Bonus: Page & Frame Co-location
If a user shares a link to your website, we may want to render the frame in the page route itself, instead of forcing them to share a link to the Frog API route.
In the example below, when a user shares the / (= /page.tsx in your Next.js App)
path of the website on Warpcast, they will be able to see the frame.
This leverages the generateMetadata function built-in to Next.js
import { getFrameMetadata } from 'frog/next'
import type { Metadata } from 'next'
export async function generateMetadata(): Promise<Metadata> {
const url = process.env.VERCEL_URL || 'http://localhost:3000'
const frameMetadata = await getFrameMetadata(`${url}/api`)
return {
other: frameMetadata,
}
}