OnchainKit is a collection of tools to build world-class onchain apps with CSS, React, and Typescript.
Add OnchainKit to your project, install the required packages.
# Use Yarn
yarn add @coinbase/onchainkit
# Use NPM
npm install @coinbase/onchainkit
# Use PNPM
pnpm add @coinbase/onchainkit
OnchainKit is divided into various theme utilities and components that are available for your use:
A Frame transforms any cast into an interactive app.
Creating a frame is easy: select an image and add clickable buttons. When a button is clicked, you receive a callback and can send another image with more buttons. To learn more, check out "Farcaster Frames Official Documentation".
React component:
<FrameMetadata />
: This component renders all the Frame metadata elements in one place.
Typescript utilities:
getFrameHtmlResponse()
: Retrieves the Frame HTML for your HTTP responses.getFrameMessage()
: Retrieves a valid Frame message from the Frame Signature Packet.getFrameMetadata()
: Retrieves valid Frame metadata for your initial HTML page with Next.js App Routing.
This component is utilized for incorporating Frame metadata elements into the React page.
Note: If you are using Next.js with App routing, it is recommended to use getFrameMetadata
instead.
export default function HomePage() {
return (
...
<FrameMetadata
buttons={[
{
label: 'Tell me the story',
},
{
action: 'link',
label: 'Link to Google',
target: 'https://www.google.com'
},
{
action: 'post_redirect',
label: 'Redirect to cute pictures',
},
]}
image="https://zizzamia.xyz/park-1.png"
input={{
text: 'Tell me a boat story',
}}
post_url="https://zizzamia.xyz/api/frame"
/>
...
);
}
@Props
type Button =
| {
action: 'link' | 'mint';
label: string;
target: string;
}
| {
action?: 'post' | 'post_redirect';
label: string;
};
type InputMetadata = {
text: string;
};
type FrameMetadataType = {
// A list of strings which are the label for the buttons in the frame (max 4 buttons).
buttons?: [Button, ...Button[]];
// An image which must be smaller than 10MB and should have an aspect ratio of 1.91:1
image: string;
// The text input to use for the Frame.
input?: InputMetadata;
// A valid POST URL to send the Signature Packet to.
post_url?: string;
// A period in seconds at which the app should expect the image to update.
refresh_period?: number;
};
type FrameMetadataReact = FrameMetadataType & {
wrapper?: React.ComponentType<any>;
};
@Returns
<meta name="fc:frame" content="vNext" />
<meta name="fc:frame:button:1" content="Tell me the story" />
<meta name="fc:frame:button:2" content="Link to Google" />
<meta name="fc:frame:button:2:action" content="link" />
<meta name="fc:frame:button:2:target" content="https://www.google.com" />
<meta name="fc:frame:button:3" content="Redirect to cute pictures" />
<meta name="fc:frame:button:3:action" content="post_redirect" />
<meta name="fc:frame:image" content="https://zizzamia.xyz/park-1.png" />
<meta name="fc:frame:input:text" content="Tell me a boat story" />
<meta name="fc:frame:post_url" content="https://zizzamia.xyz/api/frame" />
When you need to send an HTML Frame Response, the getFrameHtmlResponse
method is here to assist you.
It generates a valid HTML string response with a frame and utilizes FrameMetadata
types for page metadata. This eliminates the need to manually create server-side HTML strings.
// Step 1. import getFrameHtmlResponse from @coinbase/onchainkit
import { getFrameHtmlResponse } from '@coinbase/onchainkit';
import { NextRequest, NextResponse } from 'next/server';
async function getResponse(req: NextRequest): Promise<NextResponse> {
// Step 2. Build your Frame logic
...
return new NextResponse(
// Step 3. Use getFrameHtmlResponse to create a Frame response
getFrameHtmlResponse({
buttons: [
{
label: `We love BOAT`,
},
],
image:'https://build-onchain-apps.vercel.app/release/v-0-17.png',
post_url: 'https://build-onchain-apps.vercel.app/api/frame',
}),
);
}
export async function POST(req: NextRequest): Promise<Response> {
return getResponse(req);
}
@Param
type Button =
| {
action: 'link' | 'mint';
label: string;
target: string;
}
| {
action?: 'post' | 'post_redirect';
label: string;
};
type InputMetadata = {
text: string;
};
type FrameMetadataType = {
// A list of strings which are the label for the buttons in the frame (max 4 buttons).
buttons?: [Button, ...Button[]];
// An image which must be smaller than 10MB and should have an aspect ratio of 1.91:1
image: string;
// The text input to use for the Frame.
input?: InputMetadata;
// A valid POST URL to send the Signature Packet to.
post_url?: string;
// A period in seconds at which the app should expect the image to update.
refresh_period?: number;
};
@Returns
type FrameHTMLResponse = string;
When a user interacts with your Frame, you receive a JSON message called the "Frame Signature Packet". Decode and validate this message using the getFrameMessage
function.
You can also use getFrameMessage
to access useful information such as:
- button: number
- fid: number
- following: boolean
- liked: boolean
- recasted: boolean
- verified_accounts: string[]
Note that if the message
is not valid, it will be undefined.
// Step 1. import getFrameMessage from @coinbase/onchainkit
import { FrameRequest, getFrameMessage } from '@coinbase/onchainkit';
import { NextRequest, NextResponse } from 'next/server';
async function getResponse(req: NextRequest): Promise<NextResponse> {
// Step 2. Read the body from the Next Request
const body: FrameRequest = await req.json();
// Step 3. Validate the message
const { isValid, message } = await getFrameMessage(body , {
neynarApiKey: 'NEYNAR_ONCHAIN_KIT'
});
// Step 4. Determine the experience based on the validity of the message
if (isValid) {
// the message is valid
} else {
// sorry, the message is not valid and it will be undefined
}
...
}
export async function POST(req: NextRequest): Promise<Response> {
return getResponse(req);
}
@Param
// The Frame Signature Packet body
type FrameMessage = {
body: FrameRequest;
messageOptions?: FrameMessageOptions;
};
type FrameMessageOptions =
| {
// The API key to use for validation. Default: NEYNAR_ONCHAIN_KIT
neynarApiKey?: string;
// Whether to cast the reaction context. Default: true
castReactionContext?: boolean;
// Whether to follow the context. Default: true
followContext?: boolean;
}
| undefined;
@Returns
type Promise<FrameValidationResponse>;
type FrameValidationResponse =
| { isValid: true; message: FrameValidationData }
| { isValid: false; message: undefined };
interface FrameValidationData {
button: number; // Number of the button clicked
following: boolean; // Indicates if the viewer clicking the frame follows the cast author
input: string; // Text input from the viewer typing in the frame
interactor: {
fid: number; // Viewer Farcaster ID
custody_address: string; // Viewer custody address
verified_accounts: string[]; // Viewer account addresses
};
liked: boolean; // Indicates if the viewer clicking the frame liked the cast
raw: NeynarFrameValidationInternalModel;
recasted: boolean; // Indicates if the viewer clicking the frame recasted the cast
valid: boolean; // Indicates if the frame is valid
}
With Next.js App routing, use the getFrameMetadata()
inside your page.ts
to get the metadata need it for your Frame.
// Step 1. import getFrameMetadata from @coinbase/onchainkit
import { getFrameMetadata } from '@coinbase/onchainkit';
import type { Metadata } from 'next';
import HomePage from './home';
// Step 2. Use getFrameMetadata to shape your Frame metadata
const frameMetadata = getFrameMetadata({
buttons: [
{
label: 'We love BOAT',
},
],
image: 'https://build-onchain-apps.vercel.app/release/v-0-17.png',
post_url: 'https://build-onchain-apps.vercel.app/api/frame',
});
// Step 3. Add your metadata in the Next.js metadata utility
export const metadata: Metadata = {
manifest: '/manifest.json',
other: {
...frameMetadata
},
};
export default function Page() {
return <HomePage />;
}
@Param
type Button =
| {
action: 'link' | 'mint';
label: string;
target: string;
}
| {
action?: 'post' | 'post_redirect';
label: string;
};
type InputMetadata = {
text: string;
};
type FrameMetadataType = {
// A list of strings which are the label for the buttons in the frame (max 4 buttons).
buttons?: [Button, ...Button[]];
// An image which must be smaller than 10MB and should have an aspect ratio of 1.91:1
image: string;
// The text input to use for the Frame.
input?: InputMetadata;
// A valid POST URL to send the Signature Packet to.
post_url?: string;
// A period in seconds at which the app should expect the image to update.
refresh_period?: number;
};
@Returns
type FrameMetadataResponse = Record<string, string>;
The Name
component is used to display ENS names associated with Ethereum addresses. When an ENS name is not available, it defaults to showing a truncated version of the address.
import { Name } from '@coinbase/onchainkit';
<Name address="0x1234567890abcdef1234567890abcdef12345678" sliced={false} />;
@Props
type UseName = {
// Ethereum address to be resolved from ENS.
address: Address;
// Optional CSS class for custom styling.
className?: string;
// Determines if the address should be sliced when no ENS name is available.
sliced?: boolean;
// Additional HTML attributes for the span element.
props?: React.HTMLAttributes<HTMLSpanElement>;
};
The Avatar
component is used to display ENS avatar associated with Ethereum addresses. When an ENS avatar is not available, it defaults to blue color avatar.
import { Avatar } from '@coinbase/onchainkit';
<Avatar address="0x1234567890abcdef1234567890abcdef12345678" />;
@Props
type UseAvatar = {
// Ethereum address to be resolved from ENS.
address: Address;
// Optional CSS class for custom styling.
className?: string;
// Additional HTML attributes for the span element.
props?: React.HTMLAttributes<HTMLSpanElement>;
};
OnchainKit is all about community; for any questions, feel free to:
- Reach out to the core maintainers on Twitter or Farcaster
Leonardo Zizzamia |
Rob Polak |
Alvaro Raminelli |
Taylor Caldwell |
Chris Nascone |
Wesley Pickett |
This project is licensed under the MIT License - see the LICENSE.md file for details