Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: improvements #240

Merged
merged 18 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/new-emus-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"docs": patch
---

- adds default context reference to createFrames reference
- frames.js for apps section
- clean up example filenames, add steps
- createFrames params
- troubleshooting docs
- update useFrame reference
- make button docs more visible
- multi-page guide
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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: [
Expand All @@ -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: [
Expand All @@ -113,10 +117,10 @@ const nextConfig = {
};
```

6. Run `yarn run dev`

7. Done! 🎉
### Run `yarn run dev`

### Done! 🎉
::::

### Optional

Expand Down
62 changes: 43 additions & 19 deletions docs/pages/guides/create-frame.mdx
Original file line number Diff line number Diff line change
@@ -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."
---

Expand All @@ -9,24 +9,38 @@ 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`
Create a new Next.js app

`yarn add frames.js`
```sh
npx create-next-app@latest my-project --ts --eslint --tailwind --app
cd my-project
```

Add `frames.js` to your project

```sh
yarn add frames.js
```

### Create your Frames app

`yarn install`
```tsx [./app/frames/frames.ts]
import { createFrames } from "frames.js/next";

2. Create your Frames app
export const frames = createFrames();
```

### Create a route

```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";
import { Button } from "frames.js/next";
import { frames } from "./frames";

const frames = createFrames();
const handleRequest = frames(async (ctx) => {
return {
image: (
Expand All @@ -37,10 +51,10 @@ const handleRequest = frames(async (ctx) => {
</span>
),
buttons: [
<Button action="post" target={{ query: { value: "Yes" }}}>
<Button action="post" target={{ query: { value: "Yes" } }}>
Say Yes
</Button>,
<Button action="post" target={{ query: { value: "No" }}}>
<Button action="post" target={{ query: { value: "No" } }}>
Say No
</Button>,
],
Expand All @@ -51,18 +65,22 @@ 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() {
return {
title: "My Page",
// provide a full URL to your /frames endpoint
other: await fetchMetadata(
new URL("/frames", process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : "http://localhost:3000")
new URL(
"/frames",
process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: "http://localhost:3000"
)
),
};
}
Expand All @@ -72,6 +90,12 @@ export default function Page() {
}
```

4. Run `yarn run dev`
### Run `yarn run dev`

### Done! 🎉

::::

## Next Steps

5. Done! 🎉
- Read the [`createFrames`](/reference/core/createFrames) and [`Button`](/reference/core/Button) documentation
164 changes: 130 additions & 34 deletions docs/pages/guides/middleware.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,143 @@ 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: (
<div tw="flex">
The user's username is {ctx.message.requesterUserData.username}
</div>
),
};
});
```

### 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: (
<div>
The user's username is {ctx.message.requesterUserData.username}
</div>
),
};
},
{
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`](/reference/core/createFrames#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],
});
```

```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: <div tw="flex">foo: ${ctx.bar}</div>,
};
});

export const GET = handler;
export const POST = handler;
```

:::


### Accessing the request object

Sometimes you want to access the request object in your middleware - whenever you do this, you should clone the request object to avoid mutating it and breaking other middleware.

Here's an example of creating middleware which will add the request json to your context:

```tsx
import { createFrames, types } from "frames.js/next";

const bodyMiddleware: types.FramesMiddleware<any, { body: any }> = async (
ctx,
next
) => {
const body = await ctx.request.clone().json();
return next({ body });
};

export const frames = createFrames({
basePath: "/",
initialState: {
pageIndex: 0,
},
middleware: [bodyMiddleware],
// The request body will now be available via `ctx.body` in your frame handlers
});
```
```
Loading
Loading