Skip to content

Commit

Permalink
fix: make button docs more visible, multi-page docs
Browse files Browse the repository at this point in the history
  • Loading branch information
stephancill committed Mar 27, 2024
1 parent c7badda commit 6bffb09
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 50 deletions.
19 changes: 15 additions & 4 deletions docs/pages/guides/create-frame.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ 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

Create a new Next.js app
Expand All @@ -24,7 +26,6 @@ Add `frames.js` to your project
yarn add frames.js
```


### Create your Frames app

```tsx [./app/frames/frames.ts]
Expand All @@ -34,6 +35,7 @@ export const frames = createFrames();
```

### Create a route

```tsx [./app/frames/route.tsx]
/* eslint-disable react/jsx-key */
import { Button } from "frames.js/next";
Expand All @@ -49,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 @@ -73,7 +75,12 @@ export async function generateMetadata() {
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 @@ -88,3 +95,7 @@ export default function Page() {
### Done! 🎉

::::

## Next Steps

- Read the [`createFrames`](/reference/core/createFrames) and [`Button`](/reference/core/Button) documentation
185 changes: 143 additions & 42 deletions docs/pages/guides/multiple-frames.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,67 +5,168 @@ description: ""

# Multi-Page Frames

You will want to connect multiple frames together.
There's two different ways of navigating between frames.
Frames.js can be used to create multi-page applications by defining multiple Frames that are linked together.

The first way is by defining the `state` prop of a `Button`, and using that state to return a different Frame in the handler. See below
## Creating a Multi-Page Application

```tsx
/* eslint-disable react/jsx-key */
import { createFrames, Button } from "frames.js/next";
Frames are connected by [`Button`](/reference/core/Button) targets, similar to how Next.js `Link` components work.

:::steps

### Create your frames app

We create a new directory `./frames` with a `frames.ts` file to export our frames application from because it needs to be used from multiple routes.

const totalPages = 5;
```tsx [frames.ts]
import { createFrames } from "frames.js/next";

const frames = createFrames({
basePath: "/examples/new-api/frames",
initialState: {
pageIndex: 0,
},
export const frames = createFrames({
basePath: "/frames",
});
```

### Define your initial route

const handleRequest = frames(async (ctx) => {
const pageIndex = Number(ctx.searchParams.pageIndex || 0);
The first frame is always fetched via a GET request and is typically included alongside existing OpenGraph data via the [`generateMetadata`](https://nextjs.org/docs/app/api-reference/functions/generate-metadata) function in Next.js if you have an existing site.

const imageUrl = `https://picsum.photos/seed/frames.js-${pageIndex}/300/200`;
#### Define the initial frame

Create a `./frames/route.tsx` file that contains your initial frame. This frame will include buttons to navigate to other frames.

```tsx [route.tsx]
/* eslint-disable react/jsx-key */
import { frames } from "./frames";
import { Button } from "frames.js/next";

export const GET = frames(async () => {
return {
image: (
<div tw="flex flex-col">
<img width={300} height={200} src={imageUrl} alt="Image" />
<div tw="flex">
This is slide {pageIndex + 1} / {totalPages}
</div>
</div>
),
image: <div tw="flex">Welcome</div>,
buttons: [
// With query params
<Button
action="post"
target={{
query: { pageIndex: (pageIndex - 1) % totalPages },
}}
target={{ pathname: "/frames/route1", query: { foo: "bar" } }}
>
Go to route 1
</Button>,
<Button
action="post"
target={{
query: { pageIndex: (pageIndex + 1) % totalPages },
}}
>
// Without query params
<Button action="post" target="/frames/route2">
Go to route 2
</Button>,
],
textInput: "Type something!",
};
});
```

#### Export the initial frame metadata

In your `page.tsx` file, fetch the initial frame's metadata and include it alongside your existing page's metadata.

export const GET = handleRequest;
export const POST = handleRequest;
`fetchMetadata` is a helper function that fetches the metadata for a frame from the frames.js handler and formats it for use in the `generateMetadata` function.

```tsx [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"
)
),
};
}

export default function Page() {
return <span>My existing page</span>;
}
```

The second way to navigate between frames is by defining a `Button` with `type`, `post`, with a `target` that points at another Frame.
This can be a Frame on the same domain, or a Frame on another website entirely. In order to link between Frames in the same project, you need to set up a frames.js handler on the `POST` route of the path defined in the target.
### Create the other routes

Create additional frames in the `./frames` directory.

#### Route 1

Create a directory `./frames/route1/route.tsx` with a `POST` handler that returns the frame content.

```tsx [route1.tsx]
/* eslint-disable react/jsx-key */
import { frames } from "../frames";
import { Button } from "frames.js/next";

export const POST = frames(async (ctx) => {
const foo = ctx.searchParams.foo;

return {
image: <div tw="flex">Route 1 foo: {foo}</div>, // foo: bar
buttons: [
<Button action="post" target="/frames/route2">
Go to route 2
</Button>,
],
};
});
```

#### Route 2

Create a directory `./frames/route2/route.tsx` with a `POST` handler that returns the frame content.

```tsx [route2.tsx]
/* eslint-disable react/jsx-key */
import { frames } from "../frames";
import { Button } from "frames.js/next";

export const POST = frames(async () => {
return {
image: <div tw="flex">Route 2</div>,
buttons: [
<Button action="post" target="/frames/route1">
Go to route 1
</Button>,
],
};
});
```

### (Optional) Navigate back to the initial frame

{/*
TODO: Link to examples
*/}
If you want to navigate back to the initial frame you need to export a `POST` handler for the initial route. You may want to refactor the initial frame handler into a `frameHandler` variable that is exported as both `GET` and `POST`

```tsx [route.tsx]
import { frames } from "./frames";

const frameHandler = frames(async () => {
return {
image: <div tw="flex">Welcome</div>
buttons: [
<Button action="post" target="/frames/route1">Go to route 1</Button>,
<Button action="post" target="/frames/route2">Go to route 2</Button>,
],
};
});

export const GET = frameHandler;
export const POST = frameHandler;
```

You can then navigate back to the initial frame by linking to the initial route.

```tsx
<Button action="post" target="/frames">
Go back
</Button>
```

:::

## Notes

The second way to navigate between frames is by defining a [`Button`](/reference/core/Button) with `type`, `post`, with a `target` that points at another Frame.
This can be a Frame on the same domain, or a Frame on another website entirely. In order to link between Frames in the same project, you need to set up a frames.js handler on the `POST` route of the path defined in the target.
18 changes: 14 additions & 4 deletions docs/pages/reference/core/Button.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,22 @@ This button sends a request to a URL on which it was rendered. If you want to na

The `target` path will be resolved relatively to current URL.

### Passing state when button is clicked using query parameters
### Passing state between frames

```tsx
<Button action="post" target={{ query: { foo: "bar" }, pathname: "/next-route"}}>
<Button
action="post"
target={{ query: { foo: "bar" }, pathname: "/next-route" }}
>
Click me
</Button>
```

The state will be available in handler `ctx.pressedButton.state`.
The state will be available in the frame handler via `ctx.searchParams` e.g.

```ts
ctx.searchParams.foo; // bar
```

## Post Redirect Button

Expand All @@ -39,7 +46,10 @@ It accepts same props as `post` button.
```tsx
import { Button } from "frames.js/core";

<Button action="post_redirect" target={{ query: { foo: "bar" }, pathname: "/next-route"}}>
<Button
action="post_redirect"
target={{ query: { foo: "bar" }, pathname: "/next-route" }}
>
Click me
</Button>;
```
Expand Down

0 comments on commit 6bffb09

Please sign in to comment.