diff --git a/.changeset/grumpy-moose-beam.md b/.changeset/grumpy-moose-beam.md
new file mode 100644
index 000000000..b6a51eac8
--- /dev/null
+++ b/.changeset/grumpy-moose-beam.md
@@ -0,0 +1,5 @@
+---
+"framesjs-starter": patch
+---
+
+feat: custom fonts example
diff --git a/examples/framesjs-starter/app/examples/new-api-custom-font/frames/edge/route.tsx b/examples/framesjs-starter/app/examples/new-api-custom-font/frames/edge/route.tsx
new file mode 100644
index 000000000..286509143
--- /dev/null
+++ b/examples/framesjs-starter/app/examples/new-api-custom-font/frames/edge/route.tsx
@@ -0,0 +1,65 @@
+/* eslint-disable react/jsx-key */
+import { Button } from "frames.js/next";
+import { frames } from "../frames";
+
+// without this line, this type of importing fonts doesn't work for some reason
+export const runtime = "edge";
+
+const interRegularFont = fetch(
+ new URL("/public/Inter-Regular.ttf", import.meta.url)
+).then((res) => res.arrayBuffer());
+const interBoldFont = fetch(
+ new URL("/public/Inter-Bold.ttf", import.meta.url)
+).then((res) => res.arrayBuffer());
+const firaScriptFont = fetch(
+ new URL("/public/FiraCodeiScript-Regular.ttf", import.meta.url)
+).then((res) => res.arrayBuffer());
+
+const handleRequest = frames(async (ctx) => {
+ const [interRegularFontData, interBoldFontData, firaScriptFontData] =
+ await Promise.all([interRegularFont, interBoldFont, firaScriptFont]);
+
+ return {
+ image: (
+
+ Edge functions example custom fonts
+ Regular Inter Font
+ Bold Inter Font
+
+ Fira
+
+
+ ),
+ buttons: [
+ ,
+ ],
+ imageOptions: {
+ fonts: [
+ {
+ name: "Inter",
+ data: interRegularFontData,
+ weight: 400,
+ },
+ {
+ name: "Inter",
+ data: interBoldFontData,
+ weight: 700,
+ },
+ {
+ name: "Fira Code",
+ data: firaScriptFontData,
+ weight: 700,
+ },
+ ],
+ },
+ };
+});
+
+export const POST = handleRequest;
diff --git a/examples/framesjs-starter/app/examples/new-api-custom-font/frames/frames.ts b/examples/framesjs-starter/app/examples/new-api-custom-font/frames/frames.ts
new file mode 100644
index 000000000..56b6d5e19
--- /dev/null
+++ b/examples/framesjs-starter/app/examples/new-api-custom-font/frames/frames.ts
@@ -0,0 +1,5 @@
+import { createFrames } from "frames.js/next";
+
+export const frames = createFrames({
+ basePath: "/examples/new-api-custom-font/frames",
+});
diff --git a/examples/framesjs-starter/app/examples/new-api-custom-font/frames/nodejs/route.tsx b/examples/framesjs-starter/app/examples/new-api-custom-font/frames/nodejs/route.tsx
new file mode 100644
index 000000000..dce9f01c2
--- /dev/null
+++ b/examples/framesjs-starter/app/examples/new-api-custom-font/frames/nodejs/route.tsx
@@ -0,0 +1,71 @@
+/* eslint-disable react/jsx-key */
+import { Button } from "frames.js/next";
+import * as fs from "node:fs/promises";
+import * as path from "node:path";
+import { frames } from "../frames";
+
+export const runtime = "nodejs";
+
+const interRegularFont = fs.readFile(
+ path.join(path.resolve(process.cwd(), "public"), "Inter-Regular.ttf")
+);
+
+const interBoldFont = fs.readFile(
+ path.join(path.resolve(process.cwd(), "public"), "Inter-Bold.ttf")
+);
+
+const firaScriptFont = fs.readFile(
+ path.join(
+ path.resolve(process.cwd(), "public"),
+ "FiraCodeiScript-Regular.ttf"
+ )
+);
+
+const handleRequest = frames(async (ctx) => {
+ const [interRegularFontData, interBoldFontData, firaScriptData] =
+ await Promise.all([interRegularFont, interBoldFont, firaScriptFont]);
+
+ return {
+ buttons: [
+ ,
+ ],
+ image: (
+
+ Node.js example custom fonts
+ Regular Inter Font
+ Bold Inter Font
+
+ Fira
+
+
+ ),
+ imageOptions: {
+ fonts: [
+ {
+ name: "Inter",
+ data: interRegularFontData,
+ weight: 400,
+ },
+ {
+ name: "Inter",
+ data: interBoldFontData,
+ weight: 700,
+ },
+ {
+ name: "Fira Code",
+ data: firaScriptData,
+ weight: 700,
+ },
+ ],
+ },
+ };
+});
+
+export const POST = handleRequest;
diff --git a/examples/framesjs-starter/app/examples/new-api-custom-font/frames/route.tsx b/examples/framesjs-starter/app/examples/new-api-custom-font/frames/route.tsx
new file mode 100644
index 000000000..f0e6336b1
--- /dev/null
+++ b/examples/framesjs-starter/app/examples/new-api-custom-font/frames/route.tsx
@@ -0,0 +1,20 @@
+/* eslint-disable react/jsx-key */
+import { Button } from "frames.js/next";
+import { frames } from "./frames";
+
+const handler = frames(async (ctx) => {
+ return {
+ image:
Custom fonts example
,
+ buttons: [
+ ,
+ ,
+ ],
+ };
+});
+
+export const GET = handler;
+export const POST = handler;
diff --git a/examples/framesjs-starter/app/examples/new-api-custom-font/page.tsx b/examples/framesjs-starter/app/examples/new-api-custom-font/page.tsx
new file mode 100644
index 000000000..037c78484
--- /dev/null
+++ b/examples/framesjs-starter/app/examples/new-api-custom-font/page.tsx
@@ -0,0 +1,33 @@
+import Link from "next/link";
+import { currentURL, vercelURL } from "../../utils";
+import { createDebugUrl } from "../../debug";
+import type { Metadata } from "next";
+import { fetchMetadata } from "frames.js/next";
+
+export async function generateMetadata(): Promise {
+ return {
+ title: "New api example",
+ description: "This is a new api example",
+ other: {
+ ...(await fetchMetadata(
+ new URL(
+ "/examples/new-api-custom-font/frames",
+ vercelURL() || "http://localhost:3000"
+ )
+ )),
+ },
+ };
+}
+
+export default async function Home() {
+ const url = currentURL("/examples/new-api-custom-font");
+
+ return (
+
+ Custom font example{" "}
+
+ Debug
+
+
+ );
+}
diff --git a/examples/framesjs-starter/public/FiraCodeiScript-Regular.ttf b/examples/framesjs-starter/public/FiraCodeiScript-Regular.ttf
new file mode 100644
index 000000000..b63a44681
Binary files /dev/null and b/examples/framesjs-starter/public/FiraCodeiScript-Regular.ttf differ
diff --git a/examples/framesjs-starter/public/Inter-Bold.ttf b/examples/framesjs-starter/public/Inter-Bold.ttf
new file mode 100644
index 000000000..fe23eeb9c
Binary files /dev/null and b/examples/framesjs-starter/public/Inter-Bold.ttf differ