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 next
Manual Installation
Install Next.js
npm install next@latest react@latest react-dom@latest vercel@latest
Build 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 deploy
Bonus: 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/web'
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,
}
}