From fdea64267fabae0062c00139d5bea695cc9964a1 Mon Sep 17 00:00:00 2001
From: Stephan Cilliers <5469870+stephancill@users.noreply.github.com>
Date: Mon, 25 Mar 2024 16:45:34 +0200
Subject: [PATCH 01/18] feat: createFrames params
---
docs/pages/reference/core/createFrames.mdx | 48 +++++++++++++++++++++-
1 file changed, 46 insertions(+), 2 deletions(-)
diff --git a/docs/pages/reference/core/createFrames.mdx b/docs/pages/reference/core/createFrames.mdx
index f7b4f66ad..d38f9065c 100644
--- a/docs/pages/reference/core/createFrames.mdx
+++ b/docs/pages/reference/core/createFrames.mdx
@@ -17,6 +17,51 @@ const handleRequest = frames(async (ctx) => {
The function passed to `frames` will be called with the context of a frame action and should return a `FrameDefinition`.
+## Parameters
+
+`createFrames` accepts an optional options object with the following properties:
+
+### `basePath`
+
+- Type: `string`
+
+A string that specifies the base path for all relative URLs in the frame definition. It defaults to `/`.
+
+### `initialState`
+
+- Type: generic
+
+A JSON serializable value that is used if no state is provided in the message or you are on the initial frame.
+
+### `middleware`
+
+Type: `FramesMiddleware`
+
+An array of middleware functions that are called before the frame handler and allows you to inject additional context into the `ctx` parameter passed to each frame handler call.
+
+Each middleware should return a promise that resolves to the next middleware, or a [Web API `Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response), or a `FrameDefinition`.
+
+#### Example
+
+```tsx [./app/frames/route.tsx]
+const frames = createFrames({
+ middleware: [
+ async (ctx, next) => {
+ console.log("Before frame handler");
+ const result = await next({name: "Alice"});
+ console.log("After frame handler");
+ return result;
+ },
+ ],
+});
+
+const handler = frames(async (ctx) => {
+ return {
+ image: {ctx.name}, // Outputs an image with the text "Alice"
+ };
+});
+```
+
## FrameDefinition
`FrameDefinition` is an object that describes a frame. It has the following properties:
@@ -84,8 +129,7 @@ const handleRequest = frames(async (ctx) => {
aspectRatio: "1:1",
},
buttons: [],
- headers: {
- // [!code focus]
+ headers: {// [!code focus]
// Max cache age in seconds // [!code focus]
"Cache-Control": "max-age=0", // [!code focus]
}, // [!code focus]
From 82c41d83b8b597c463fa98b40e45fe8b5cff6ca5 Mon Sep 17 00:00:00 2001
From: Stephan Cilliers <5469870+stephancill@users.noreply.github.com>
Date: Mon, 25 Mar 2024 16:50:54 +0200
Subject: [PATCH 02/18] fix: clean up example filenames, add steps
---
docs/pages/guides/create-frame.mdx | 34 ++++++++++--------
docs/pages/guides/display-frames.mdx | 44 ++++++++++++-----------
docs/pages/index.mdx | 23 +++++-------
docs/pages/reference/core/Button.mdx | 3 +-
docs/pages/reference/core/next/index.mdx | 6 ++--
docs/pages/reference/core/remix/index.mdx | 6 ++--
6 files changed, 57 insertions(+), 59 deletions(-)
diff --git a/docs/pages/guides/create-frame.mdx b/docs/pages/guides/create-frame.mdx
index 0528d582d..cc6403853 100644
--- a/docs/pages/guides/create-frame.mdx
+++ b/docs/pages/guides/create-frame.mdx
@@ -1,5 +1,5 @@
---
-title: "Guide: Display Frames in your app"
+title: "Guide: Create your first Frame"
description: "Frames.js is the react based framework for making frames. Debugger included."
---
@@ -8,21 +8,26 @@ description: "Frames.js is the react based framework for making frames. Debugger
This guide shows you how to add frames rendering to your next.js + tailwind app using frames.js.
## Steps
+::::steps
+### Create a new repo
-1. Create a new repo
+Create a new Next.js app
-`npx create-next-app@latest my-project --ts --eslint --tailwind --app`
+```sh
+npx create-next-app@latest my-project --ts --eslint --tailwind --app
+cd my-project
+```
-`cd my-project`
+Add `frames.js` to your project
-`yarn add frames.js`
+```sh
+yarn add frames.js
+```
-`yarn install`
-2. Create your Frames app
+### Create your Frames app
-```tsx filename="// ./app/frames/route.tsx"
-// ./app/frames/route.tsx
+```tsx [./app/frames/route.tsx]
/* eslint-disable react/jsx-key */
import { createFrames, Button } from "frames.js/next";
@@ -51,10 +56,9 @@ export const GET = handleRequest;
export const POST = handleRequest;
```
-3. If you have an existing page, render Frames in your metadata
+### If you have an existing page, render Frames in your metadata
-```tsx filename="// ./app/page.tsx"
-// ./app/page.tsx
+```tsx [./app/page.tsx]
import { fetchMetadata } from "frames.js/next";
export async function generateMetadata() {
@@ -72,6 +76,8 @@ export default function Page() {
}
```
-4. Run `yarn run dev`
+### Run `yarn run dev`
+
+### Done! 🎉
-5. Done! 🎉
+::::
diff --git a/docs/pages/guides/display-frames.mdx b/docs/pages/guides/display-frames.mdx
index 6506f5617..298bd9fdd 100644
--- a/docs/pages/guides/display-frames.mdx
+++ b/docs/pages/guides/display-frames.mdx
@@ -9,26 +9,32 @@ This guide shows you how to add frames rendering to your next.js + tailwind app
## Steps
-1. Create a new repo
+::::steps
-`npx create-next-app@latest my-project --ts --eslint --tailwind --app`
+### Create a new repo
-`cd my-project`
-`yarn add @frames.js/render`
+Create a new Next.js app
-`yarn install`
+```sh
+npx create-next-app@latest my-project --ts --eslint --tailwind --app
+cd my-project
+```
+
+Add `@frames.js/render` to your project
-2. Add proxies for routing frame requests via your backend for privacy + preventing CORS issues
-```tsx filename="// ./app/frames/route.tsx"
-// ./app/frames/route.tsx
+```sh
+yarn add @frames.js/render
+```
+
+### Add proxies for routing frame requests via your backend for privacy + preventing CORS issues
+```tsx [./app/frames/route.tsx]
export { GET, POST } from "@frames.js/render/next";
```
-3. Add the renderer to your page
+### Add the renderer to your page
-```tsx filename="// ./app/page.tsx"
-// ./app/page.tsx
+```tsx [./app/page.tsx]
"use client";
import {
FrameUI,
@@ -81,10 +87,9 @@ export default function Page() {
```
-4. In order for the styles to work, your project should have tailwind set up as well as the tailwind.config.js rule
+### In order for the styles to work, your project should have tailwind set up as well as the tailwind.config.js rule
-```tsx filename="// tailwind.config.js"
-// tailwind.config.js
+```tsx [tailwind.config.js]
const config = {
// ...
content: [
@@ -97,10 +102,9 @@ const config = {
}
```
-5. Allow images from any domain
+### Allow images from any domain
-```tsx filename="// next.config.js"
-// next.config.js
+```tsx [next.config.js]
const nextConfig = {
images: {
remotePatterns: [
@@ -113,10 +117,10 @@ const nextConfig = {
};
```
-6. Run `yarn run dev`
-
-7. Done! 🎉
+### Run `yarn run dev`
+### Done! 🎉
+::::
### Optional
diff --git a/docs/pages/index.mdx b/docs/pages/index.mdx
index d91135701..d4ce3b591 100644
--- a/docs/pages/index.mdx
+++ b/docs/pages/index.mdx
@@ -65,9 +65,7 @@ pnpm create frames
yarn add frames.js
```
-```tsx filename="// ./app/page.tsx"
-// ./app/page.tsx
-
+```tsx [./app/page.tsx]
import { fetchMetadata } from "frames.js/next";
export async function generateMetadata() {
@@ -89,8 +87,7 @@ export default function Home(props) {
}
```
-```ts filename="./app/frames/route.tsx"
-// ./app/frames/route.tsx
+```ts [./app/frames/route.tsx]
/* eslint-disable react/jsx-key */
import { createFrames, Button } from 'frames.js/next';
@@ -126,14 +123,12 @@ Check out the following places for more Frames-related content:
Or use the [hosted Frames debugger](https://debugger.framesjs.org/?url=https%3A%2F%2Fframesjs.org). Running locally has the benefits of it working with natively with localhost.
-## Prefer to not use JSX?
-
-### Use frames.js in Next.js using helper functions
+## Prefer to not use JSX?
-```tsx filename="./app/page.tsx"
-// page that renders a frame
-// ./app/page.tsx
+### frames.js in Next.js using helper functions
+```tsx [./app/page.tsx]
+// Page that returns a frame
import { Frame, getFrameFlattened } from "frames.js";
import type { Metadata } from "next";
@@ -164,10 +159,8 @@ export const metadata: Metadata = {
};
```
-```ts filename="app/frames/route.ts"
-// handle frame actions
-// ./app/frames/route.ts
-
+```ts [./app/frames/route.ts]
+// Route that handles frame actions
import { getFrameHtml, validateFrameMessage } from "frames.js";
import { NextRequest } from "next/server";
diff --git a/docs/pages/reference/core/Button.mdx b/docs/pages/reference/core/Button.mdx
index 38daa078a..7fad906b4 100644
--- a/docs/pages/reference/core/Button.mdx
+++ b/docs/pages/reference/core/Button.mdx
@@ -114,8 +114,7 @@ type EthSendTransactionParams {
If the transaction is successful, the frame will send a POST request to the URL specified in `post_url` or the frame `post_url`. This can be handled by the frame to show a success message or redirect the user to a different page.
-```tsx
-// /route.tsx
+```tsx [route.tsx]
import { Button } from "frames.js/next";
import { createFrames } from "frames.js/next";
diff --git a/docs/pages/reference/core/next/index.mdx b/docs/pages/reference/core/next/index.mdx
index 12a719491..1324c6a91 100644
--- a/docs/pages/reference/core/next/index.mdx
+++ b/docs/pages/reference/core/next/index.mdx
@@ -8,8 +8,7 @@ Frames.js can be easily integrated with [Next.js](https://nextjs.org) applicatio
Frames handler is responsible for rendering your Frames and also reacts to user interactions with buttons.
-```tsx
-// app/frames/route.tsx
+```tsx [./app/frames/route.tsx]
/* eslint-disable react/jsx-key */
import { createFrames, Button } from "frames.js/next";
@@ -29,8 +28,7 @@ export const POST = handleRequest;
In order to render the initial frame of your Frames app on some of your pages, you need to render frame metadata. That can be achieved using [`generateMetadata() API`](https://nextjs.org/docs/app/api-reference/functions/generate-metadata).
-```tsx
-// app/page.tsx
+```tsx [./app/page.tsx]
import { fetchMetadata } from "frames.js/next";
export async function generateMetadata() {
diff --git a/docs/pages/reference/core/remix/index.mdx b/docs/pages/reference/core/remix/index.mdx
index 5649e9e0a..2282f209a 100644
--- a/docs/pages/reference/core/remix/index.mdx
+++ b/docs/pages/reference/core/remix/index.mdx
@@ -8,8 +8,7 @@ Frames.js can be easily integrated with [Remix](https://remix.run) applications.
Frames handler is responsible for rendering your Frames and also reacts to user interactions with buttons.
-```tsx
-// app/routes/frames.tsx
+```tsx [./app/routes/frames.tsx]
/* eslint-disable react/jsx-key */
import { createFrames, Button } from "frames.js/remix";
@@ -27,8 +26,7 @@ export const loader = handleRequest;
### Render initial frame on your existing page
-```tsx
-// app/routes/_index.tsx
+```tsx [./app/routes/_index.tsx]
import { fetchMetadata } from "frames.js/remix";
export async function loader({ request }) {
From 166b785443f1c7d953f42f3a330ebac8f5dc10ee Mon Sep 17 00:00:00 2001
From: Stephan Cilliers <5469870+stephancill@users.noreply.github.com>
Date: Mon, 25 Mar 2024 17:04:13 +0200
Subject: [PATCH 03/18] fix: frames.js for apps, @frames.js/render imports in
docs
---
.../guides/{ => apps}/display-frames.mdx | 0
docs/pages/reference/render/frame-ui.mdx | 8 ++--
.../reference/render/next/frame-image.mdx | 8 ++--
docs/pages/reference/render/next/get.mdx | 4 +-
docs/pages/reference/render/next/post.mdx | 4 +-
docs/pages/reference/render/types.mdx | 6 +--
docs/pages/reference/render/use-frame.mdx | 23 ++++-----
docs/vocs.config.tsx | 48 +++++++++++--------
8 files changed, 53 insertions(+), 48 deletions(-)
rename docs/pages/guides/{ => apps}/display-frames.mdx (100%)
diff --git a/docs/pages/guides/display-frames.mdx b/docs/pages/guides/apps/display-frames.mdx
similarity index 100%
rename from docs/pages/guides/display-frames.mdx
rename to docs/pages/guides/apps/display-frames.mdx
diff --git a/docs/pages/reference/render/frame-ui.mdx b/docs/pages/reference/render/frame-ui.mdx
index eb062c277..3611f3807 100644
--- a/docs/pages/reference/render/frame-ui.mdx
+++ b/docs/pages/reference/render/frame-ui.mdx
@@ -3,9 +3,9 @@
## Usage
```tsx [frames.js/render/types.tsx]
-import { FrameUI, fallbackFrameContext } from 'frames.js/render';
-import { FrameImageNext } from "frames.js/render/next";
-import { useFrame } from "frames.js/render/use-frame";
+import { FrameUI, fallbackFrameContext } from "@frames.js/render';
+import { FrameImageNext } from "@frames.js/render/next";
+import { useFrame } from "@frames.js/render/use-frame";
export const Page(){
@@ -35,4 +35,4 @@ const config = {
"./node_modules/frames.js/dist/render/*.{ts,tsx,js,css}",
"./node_modules/frames.js/dist/**/*.{ts,tsx,js,css}",
]
-```
\ No newline at end of file
+```
diff --git a/docs/pages/reference/render/next/frame-image.mdx b/docs/pages/reference/render/next/frame-image.mdx
index 10141f77e..278daa50f 100644
--- a/docs/pages/reference/render/next/frame-image.mdx
+++ b/docs/pages/reference/render/next/frame-image.mdx
@@ -3,9 +3,9 @@
## Usage
```tsx [frames.js/render/types.tsx]
-import { FrameUI, fallbackFrameContext } from 'frames.js/render';
-import { useFrame } from 'frames.js/render/use-frame';
-import { FrameImageNext } from "frames.js/render/next";
+import { FrameUI, fallbackFrameContext } from "@frames.js/render';
+import { useFrame } from "@frames.js/render/use-frame';
+import { FrameImageNext } from "@frames.js/render/next";
export const Page(){
// ...
@@ -18,4 +18,4 @@ export const Page(){
);
}
-```
\ No newline at end of file
+```
diff --git a/docs/pages/reference/render/next/get.mdx b/docs/pages/reference/render/next/get.mdx
index bbf8b888d..b6dabd9fd 100644
--- a/docs/pages/reference/render/next/get.mdx
+++ b/docs/pages/reference/render/next/get.mdx
@@ -3,5 +3,5 @@
## Usage
```tsx [./frames/route.tsx]
-export { GET, POST } from 'frames.js/render/next';
-```
\ No newline at end of file
+export { GET, POST } from "@frames.js/render/next';
+```
diff --git a/docs/pages/reference/render/next/post.mdx b/docs/pages/reference/render/next/post.mdx
index 9c36324b2..d372a38fb 100644
--- a/docs/pages/reference/render/next/post.mdx
+++ b/docs/pages/reference/render/next/post.mdx
@@ -3,5 +3,5 @@
## Usage
```tsx [./frames/route.tsx]
-export { GET, POST } from 'frames.js/render/next';
-```
\ No newline at end of file
+export { GET, POST } from "@frames.js/render/next';
+```
diff --git a/docs/pages/reference/render/types.mdx b/docs/pages/reference/render/types.mdx
index bcb838fe1..14b3668f3 100644
--- a/docs/pages/reference/render/types.mdx
+++ b/docs/pages/reference/render/types.mdx
@@ -3,7 +3,7 @@
## Example usage
```tsx
-import { SignerStateInstance } from "frames.js/render";
+import { SignerStateInstance } from "@frames.js/render";
```
## Reference
@@ -142,6 +142,4 @@ export type FrameTheme = Partial>;
export interface FrameActionBodyPayload {}
export type FrameContext = FarcasterFrameContext;
-
-
-```
\ No newline at end of file
+```
diff --git a/docs/pages/reference/render/use-frame.mdx b/docs/pages/reference/render/use-frame.mdx
index 1e37aaee4..299b47d86 100644
--- a/docs/pages/reference/render/use-frame.mdx
+++ b/docs/pages/reference/render/use-frame.mdx
@@ -18,22 +18,21 @@ type Props = {
onMint?: (t: onMintArgs) => void;
/** the context of this frame, used for generating Frame Action payloads */
frameContext: FrameContext;
-}
+};
```
## Usage
```tsx [frames.js/render/types.tsx]
"use client";
-import {
- FrameUI,
- fallbackFrameContext,
- FrameContext,
-} from "frames.js/render";
-import { FrameImageNext } from "frames.js/render/next";
+import { FrameUI, fallbackFrameContext, FrameContext } from "@frames.js/render";
+import { FrameImageNext } from "@frames.js/render/next";
import { FrameButton } from "frames.js";
-import { useFrame } from "frames.js/render/use-frame";
-import { mockFarcasterSigner, createFrameActionMessageWithSignerKey } from "frames.js/render/farcaster";
+import { useFrame } from "@frames.js/render/use-frame";
+import {
+ mockFarcasterSigner,
+ createFrameActionMessageWithSignerKey,
+} from "@frames.js/render/farcaster";
export default function Page() {
const frameState = useFrame({
@@ -54,7 +53,9 @@ export default function Page() {
// Implement me
alert("A frame button was pressed without a signer.");
},
- signFrameAction: () => { alert('implement me.') },
+ signFrameAction: () => {
+ alert("implement me.");
+ },
},
});
@@ -64,4 +65,4 @@ export default function Page() {
}
```
-[Full example](/guides/display-frames.mdx)
\ No newline at end of file
+[Full example](/guides/apps/display-frames.mdx)
diff --git a/docs/vocs.config.tsx b/docs/vocs.config.tsx
index 000647e80..da3cf2784 100644
--- a/docs/vocs.config.tsx
+++ b/docs/vocs.config.tsx
@@ -50,16 +50,22 @@ const sidebar = [
text: "Transactions",
link: "/guides/transactions",
},
- {
- text: "Display Frames",
- link: "/guides/display-frames",
- },
{
text: "Open Frames",
link: "/guides/open-frames",
},
],
},
+ {
+ text: "Frames.js for Apps",
+ collapsed: false,
+ items: [
+ {
+ text: "Display Frames",
+ link: "/guides/apps/display-frames",
+ },
+ ],
+ },
{
text: "Write your frame with",
collapsed: false,
@@ -232,7 +238,7 @@ const sidebar = [
],
},
{
- text: "frames.js/render",
+ text: "@frames.js/render",
collapsed: true,
items: [
{
@@ -247,23 +253,23 @@ const sidebar = [
text: "FrameUI",
link: "/reference/render/frame-ui",
},
- ],
- },
- {
- text: "frames.js/render/next",
- collapsed: true,
- items: [
- {
- text: "FrameImage",
- link: "/reference/render/next/frame-image",
- },
{
- text: "POST",
- link: "/reference/render/next/POST",
- },
- {
- text: "GET",
- link: "/reference/render/next/GET",
+ text: "Next.js",
+ collapsed: true,
+ items: [
+ {
+ text: "FrameImage",
+ link: "/reference/render/next/frame-image",
+ },
+ {
+ text: "POST",
+ link: "/reference/render/next/POST",
+ },
+ {
+ text: "GET",
+ link: "/reference/render/next/GET",
+ },
+ ],
},
],
},
From e0753e9f10acbf407754d676a88e3776f33ff168 Mon Sep 17 00:00:00 2001
From: Stephan Cilliers <5469870+stephancill@users.noreply.github.com>
Date: Mon, 25 Mar 2024 17:29:09 +0200
Subject: [PATCH 04/18] feat: context reference
---
.changeset/new-emus-love.md | 8 +++
docs/pages/reference/core/createFrames.mdx | 68 ++++++++++++++++++++++
2 files changed, 76 insertions(+)
create mode 100644 .changeset/new-emus-love.md
diff --git a/.changeset/new-emus-love.md b/.changeset/new-emus-love.md
new file mode 100644
index 000000000..e0434dd52
--- /dev/null
+++ b/.changeset/new-emus-love.md
@@ -0,0 +1,8 @@
+---
+"docs": patch
+---
+
+- Adds default context reference to `createFrames` reference
+- "Frames.js for apps" section
+- Clean up example filenames, add steps
+- `createFrames` params
diff --git a/docs/pages/reference/core/createFrames.mdx b/docs/pages/reference/core/createFrames.mdx
index d38f9065c..32a9870d2 100644
--- a/docs/pages/reference/core/createFrames.mdx
+++ b/docs/pages/reference/core/createFrames.mdx
@@ -146,3 +146,71 @@ handleRequest(new Request("/")).then((res) => {
return res.text();
});
```
+
+## Context
+
+Core middleware is included and executed by default and gives you access to the following default context in your frame handlers:
+
+### `basePath`
+
+- Type: `string`
+
+Specifies the base path for all relative URLs in the frame definition.
+
+### `initialState`
+
+- Type: generic
+
+A JSON serializable value that is used if no state is provided in the message or you are on the initial frame.
+
+### `request`
+
+- Type: [Web API `Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
+
+The request object that was passed to the request handler.
+
+### `url`
+
+- Type: [Web API `URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL)
+
+The URL object that was parsed from the request.
+
+### `searchParams`
+
+- Type: `Record`
+
+The search params in the URL as an object. If there are no search params, it will be an empty object.
+
+### `pressedButton`
+
+- Type: `undefined | { action: "post" | "post_redirect"; index: 1 | 2 | 3 | 4 }`
+
+The button that was clicked on the previous frame.
+
+### `buttonIndex`
+
+- Type: `number`
+
+The index of the button that was clicked on the previous frame.
+
+### `message`
+
+- Type: `FrameMessage`
+
+The frame message that was parsed from the request body.
+
+### `clientProtocol`
+
+- Type: `ClientProtocolId`
+
+The client protocol that was used to send the frame message.
+
+### `state`
+
+- Type: `JsonValue`
+
+The state extracted from the frame message. If you are on the initial frame (no button pressed), the value is the `initialState` value passed to `createFrames`. If you are on a frame with a button pressed, the value is the state from the previous frame.
+
+
+
+
From d65714fb36d8295ef616c4e83fb215e9280ce43b Mon Sep 17 00:00:00 2001
From: Stephan Cilliers <5469870+stephancill@users.noreply.github.com>
Date: Mon, 25 Mar 2024 21:54:40 +0200
Subject: [PATCH 05/18] feat: add note about per-route middleware
---
docs/pages/reference/core/createFrames.mdx | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/docs/pages/reference/core/createFrames.mdx b/docs/pages/reference/core/createFrames.mdx
index 32a9870d2..6b30573d9 100644
--- a/docs/pages/reference/core/createFrames.mdx
+++ b/docs/pages/reference/core/createFrames.mdx
@@ -147,6 +147,24 @@ handleRequest(new Request("/")).then((res) => {
});
```
+### Per-route middleware
+
+You can also pass middleware to the `handleRequest` function to be executed only for that specific route.
+
+```tsx
+const handleRequest = frames(async (ctx) => {
+ return {
+ image: Test,
+ };
+}, {
+ middleware: [
+ farcasterHubContext({ hubHttpUrl: process.env.HUB_HTTP_URL }),
+ ]
+});
+```
+
+This will only execute the `farcasterHubContext` middleware for the route that `handleRequest` is called with.
+
## Context
Core middleware is included and executed by default and gives you access to the following default context in your frame handlers:
From 856428a507956d14477ffd84cf0d6a44cd9ca57c Mon Sep 17 00:00:00 2001
From: Stephan Cilliers <5469870+stephancill@users.noreply.github.com>
Date: Tue, 26 Mar 2024 15:51:18 +0200
Subject: [PATCH 06/18] feat: troubleshooting
---
docs/pages/troubleshooting.mdx | 139 +++++++++++++++++++++++++++++++++
docs/vocs.config.tsx | 4 +
2 files changed, 143 insertions(+)
create mode 100644 docs/pages/troubleshooting.mdx
diff --git a/docs/pages/troubleshooting.mdx b/docs/pages/troubleshooting.mdx
new file mode 100644
index 000000000..bb6479f4d
--- /dev/null
+++ b/docs/pages/troubleshooting.mdx
@@ -0,0 +1,139 @@
+# Troubleshooting
+
+## Image not rendering
+
+### Image too large
+
+If the image is not rendering, it may be too large.
+
+- If you are including a full size external `` element, consider passing the external image as a string instead:
+
+```tsx
+return {
+ image: "https://example.com/image.jpg",
+};
+```
+
+- Try resizing it to a smaller size via the `imageOptions` of the returned `FrameDefinition`.
+
+```tsx
+return {
+ image:
...
,
+ imageOptions: {
+ width: 100,
+ height: 100,
+ },
+};
+```
+
+### Image wrong format
+
+If the image is not rendering, it may be in the wrong format. SVG images are typically not supported on mobile.
+
+## Initial frame not loading
+
+Ensure that the `fetchMetadata` URL is correct and that it is inside the `other` folder for Next.js.
+
+### `VERCEL_URL` environment variable
+
+If you are using Vercel, the `VERCEL_URL` environment variable is not a fully qualified URL and may cause issues with `fetchMetadata`. You will have to prepend the protocol `VERCEL_URL`.
+
+```tsx
+export async function generateMetadata() {
+ const frameMetadata = await fetchMetadata(
+ new URL(
+ "/frames",
+ process.env.VERCEL_URL
+ ? `https://${process.env.VERCEL_URL}`
+ : "http://localhost:3000"
+ )
+ );
+
+ return {
+ title: "My page",
+ other: {
+ ...frameMetadata,
+ },
+ };
+}
+```
+
+### Vercel authentication
+
+When deploying to Vercel, your site will not automatically be accessible to the public. You will need to disable Vercel Authentication under `Settings > Deployment Protection > Vercel Authentication`
+
+## Import type errors
+
+If you are getting type errors when importing `frames.js`, you may need to change the `moduleResolution` in your `tsconfig.json` from `node` to `nodenext`.
+
+```json
+{
+ "compilerOptions": {
+ "moduleResolution": "nodenext"
+ }
+}
+```
+
+## Unable to access frame message on initial frame
+
+The initial frame is accessed via a GET request and does not have access to frame message and hence user data.
+
+## Type error: Route ... does not match the required types of a Next.js Route
+
+If you are getting this error you are exporting something other than a Next.js route from a `route.tsx` or `page.tsx` file.
+
+We recommend creating a new file for your `frames` app and importing it in the routes that use it.
+
+### Example
+
+Frames app file
+
+```tsx [frames.ts]
+import { createFrames } from "frames.js/next";
+
+export const frames = createFrames({
+ basePath: "/frames",
+});
+```
+
+Initial page route
+
+```tsx [route.tsx]
+import { frames } from "./frames";
+
+export const GET = frames(async (ctx) => {
+ // ctx.message is not available in the initial frame
+ return {
+ image:
,
+ buttons: [
+ // ...
+ ],
+ };
+});
+```
+
+## Combining old and new SDKs
+
+You cannot use the ``, `` and `` components from the old SDK (`frames.js/next/server`) with the new SDK (`frames.js/next`).
+
+The new SDK uses a [`FrameDefinition`](/reference/core/createFrames#framedefinition) object to define a frame.
diff --git a/docs/vocs.config.tsx b/docs/vocs.config.tsx
index da3cf2784..e3eccd0ec 100644
--- a/docs/vocs.config.tsx
+++ b/docs/vocs.config.tsx
@@ -102,6 +102,10 @@ const sidebar = [
},
],
},
+ {
+ text: "Troubleshooting",
+ link: "/troubleshooting",
+ },
{
text: "Reference",
// link: "/reference",
From b2e96c0051fde191f04d92c0d1d2569498f46da4 Mon Sep 17 00:00:00 2001
From: Stephan Cilliers <5469870+stephancill@users.noreply.github.com>
Date: Tue, 26 Mar 2024 15:51:50 +0200
Subject: [PATCH 07/18] fix: FrameDefinition types
---
docs/pages/reference/core/createFrames.mdx | 83 +++++++++++++---------
1 file changed, 49 insertions(+), 34 deletions(-)
diff --git a/docs/pages/reference/core/createFrames.mdx b/docs/pages/reference/core/createFrames.mdx
index 6b30573d9..c30ea287d 100644
--- a/docs/pages/reference/core/createFrames.mdx
+++ b/docs/pages/reference/core/createFrames.mdx
@@ -66,43 +66,58 @@ const handler = frames(async (ctx) => {
`FrameDefinition` is an object that describes a frame. It has the following properties:
+### `image`
+
+- Type: `React.ReactElement | string`
+
+The image to be rendered in the frame. If a string is provided, it must be a valid URL.
+
+### `imageOptions`
+
+- Type: `{ aspectRatio?: "1.91:1" | "1:1" } & ConstructorParameters[1]`
+
+Options for the image. The `aspectRatio` property can be set to `"1.91:1"` or `"1:1"`.
+
+### `buttons`
+
+- Type: 1, 2, 3, or 4 `FrameButtonElement` elements
+
+An array of buttons to be rendered in the frame. The buttons are rendered in the order they are provided.
+
+#### Example
+
```tsx
-/**
- * Frame definition, this is rendered by the frames
- */
-export type FrameDefinition = {
- /**
- * If string then it must be a valid URL
- */
- image: React.ReactElement | string;
- imageOptions?: {
- /**
- * @default '1.91:1'
- */
- aspectRatio?: "1.91:1" | "1:1";
- } & ConstructorParameters[1];
- buttons?:
- | []
- | [FrameButtonElement]
- | [FrameButtonElement, FrameButtonElement]
- | [FrameButtonElement, FrameButtonElement, FrameButtonElement]
- | [
- FrameButtonElement,
- FrameButtonElement,
- FrameButtonElement,
- FrameButtonElement,
- ];
- /**
- * Label for text input, if no value is provided the input is not rendered
- */
- textInput?: string;
- /**
- * Global app state that will be available on next frame
- */
- state?: JsonValue;
-} & ResponseInit;
+import { Button } from "frames.js/next";
+
+const handleRequest = frames(async (ctx) => {
+ return {
+ image: Test,
+ buttons: [
+ ,
+ ,
+ ],
+ };
+});
```
+### `textInput`
+
+- Type: `string`
+
+Label for text input. If no value is provided, the input is not rendered.
+
+### `state`
+
+- Type: `JsonValue`
+
+Global app state that will be available on the next frame.
+
+### `headers`
+
+- Type: `HeadersInit`
+
+Custom headers to be included in the response.
+
The `ResponseInit` properties allow you to specify custom headers such as `Cache-Control`.
### Cache-Control
From e9acd3a7fbcefd3c43b8e57aee12c118cd93f860 Mon Sep 17 00:00:00 2001
From: Stephan Cilliers <5469870+stephancill@users.noreply.github.com>
Date: Tue, 26 Mar 2024 16:08:10 +0200
Subject: [PATCH 08/18] fix: troubleshooting file name
---
docs/pages/troubleshooting.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/troubleshooting.mdx b/docs/pages/troubleshooting.mdx
index bb6479f4d..53333f68a 100644
--- a/docs/pages/troubleshooting.mdx
+++ b/docs/pages/troubleshooting.mdx
@@ -66,7 +66,7 @@ When deploying to Vercel, your site will not automatically be accessible to the
If you are getting type errors when importing `frames.js`, you may need to change the `moduleResolution` in your `tsconfig.json` from `node` to `nodenext`.
-```json
+```json [tsconfig.json]
{
"compilerOptions": {
"moduleResolution": "nodenext"
From fa4e870b2b607d04af55faa9286be67ae5ab9e94 Mon Sep 17 00:00:00 2001
From: Stephan Cilliers <5469870+stephancill@users.noreply.github.com>
Date: Tue, 26 Mar 2024 17:24:15 +0200
Subject: [PATCH 09/18] feat: middleware docs, minor improvements
---
docs/pages/guides/create-frame.mdx | 11 +-
docs/pages/guides/middleware.mdx | 134 +++++++++++++++------
docs/pages/reference/core/Button.mdx | 2 +-
docs/pages/reference/core/createFrames.mdx | 42 ++++---
docs/pages/troubleshooting.mdx | 2 +-
5 files changed, 139 insertions(+), 52 deletions(-)
diff --git a/docs/pages/guides/create-frame.mdx b/docs/pages/guides/create-frame.mdx
index cc6403853..f6e9fbae4 100644
--- a/docs/pages/guides/create-frame.mdx
+++ b/docs/pages/guides/create-frame.mdx
@@ -27,11 +27,18 @@ yarn add frames.js
### Create your Frames app
+```tsx [./app/frames/frames.ts]
+import { createFrames } from "frames.js/next";
+
+export const frames = createFrames();
+```
+
+### Create a route
```tsx [./app/frames/route.tsx]
/* eslint-disable react/jsx-key */
-import { createFrames, Button } from "frames.js/next";
+import { Button } from "frames.js/next";
+import { frames } from "./frames";
-const frames = createFrames();
const handleRequest = frames(async (ctx) => {
return {
image: (
diff --git a/docs/pages/guides/middleware.mdx b/docs/pages/guides/middleware.mdx
index d3e0de689..11f253e50 100644
--- a/docs/pages/guides/middleware.mdx
+++ b/docs/pages/guides/middleware.mdx
@@ -7,47 +7,113 @@ description: ""
Frames.js uses middleware to extend the functionality of Frames, bringing in data from API providers, verifying frame actions and adding Open Frames support.
-You can use middleware for all your frames by passing in middleware via the `middleware` Option.
+You can use middleware for all your frames by passing in middleware via the `middleware` option in `createFrames` or you can specify per-route middleware.
-```tsx
-import { farcasterHubContext, openframes } from "frames.js/middleware";
-import { createFrames } from "frames.js/next";
-import { getXmtpFrameMessage, isXmtpFrameActionPayload } from "frames.js/xmtp";
+## Using middleware
-const DEFAULT_DEBUGGER_URL =
- process.env.DEBUGGER_URL ?? "http://localhost:3010/";
+Include the middleware in your `createFrames` call:
-export const DEFAULT_DEBUGGER_HUB_URL =
- process.env.NODE_ENV === "development"
- ? new URL("/hub", DEFAULT_DEBUGGER_URL).toString()
- : undefined;
+```tsx [frames.ts]
+import { farcasterHubContext } from "frames.js/middleware";
+import { createFrames } from "frames.js/next";
const frames = createFrames({
basePath: "/",
initialState: {
pageIndex: 0,
},
- middleware: [
- farcasterHubContext({
- hubHttpUrl: DEFAULT_DEBUGGER_HUB_URL,
- }),
- openframes({
- clientProtocol: {
- id: "xmtp",
- version: "2024-02-09",
- },
- handler: {
- isValidPayload: (body: JSON) => isXmtpFrameActionPayload(body),
- getFrameMessage: async (body: JSON) => {
- if (!isXmtpFrameActionPayload(body)) {
- return undefined;
- }
- const result = await getXmtpFrameMessage(body);
-
- return { ...result };
- },
- },
- }),
- ],
+ middleware: [farcasterHubContext()],
+});
+```
+
+```tsx [frames/username/route.tsx]
+import { frames } from "./frames";
+
+export const POST = frames(async (ctx) => {
+ // The added context from the middleware will be available on `ctx` here
+ if (!ctx.message.isValid) {
+ throw new Error("Invalid message");
+ }
+
+ return {
+ image: (
+
The user's username is {ctx.message.requesterUserData.username}
+ ),
+ };
+});
+```
+
+### Per-route middleware
+
+You can also specify middleware per-route that will only be applied to that route:
+
+```tsx [frames/username/route.tsx]
+import { farcasterHubContext } from "frames.js/middleware";
+
+export const POST = frames(
+ async (ctx) => {
+ // The added context from the middleware will be available on `ctx` here
+ if (!ctx.message.isValid) {
+ throw new Error("Invalid message");
+ }
+
+ return {
+ image: (
+
+ The user's username is {ctx.message.requesterUserData.username}
+
+ ),
+ };
+ },
+ {
+ middleware: [farcasterHubContext()],
+ }
+);
+```
+
+## Defining your own middleware
+
+You can define your own middleware by creating a function that returns a promise that resolves to the next middleware, or a [Web API `Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response), or a `FrameDefinition`.
+
+Middleware can modify the context or return a response that will terminate the request early.
+
+### Adding context
+
+:::code-group
+
+```tsx [frames.ts]
+import { createFrames, types } from "frames.js/next";
+
+const myMiddleware: types.FramesMiddleware<
+ any,
+ { foo: string }
+> = async (ctx, next) => {
+ return next({ foo: "bar" });
+};
+
+export const frames = createFrames({
+ basePath: "/",
+ initialState: {
+ pageIndex: 0,
+ },
+ // Add the middleware
+ middleware: [myMiddleware],
});
-```
\ No newline at end of file
+```
+
+```tsx [frames/route.tsx]
+import { Button } from "frames.js/next";
+import { frames } from "./frames";
+
+const handler = frames(async (ctx) => {
+ return {
+ // Use the additional contect
+ image: