> ## Documentation Index
> Fetch the complete documentation index at: https://basecamp.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Programming for Frames

> フレームのプログラミングをはじめよう

<Warning>
  今回のレッスンで作成する最終的なソースコードを確認したい場合は、ページの最後の [最終的なソースコード](#10) から GitHub リポジトリにアクセスしてソースコードをダウンロードしてください。
</Warning>

<iframe className="w-full aspect-video" src="https://www.loom.com/embed/8e8a83d1d6be413381f12c652234fa60?sid=92f5e589-abd7-44a7-bbd3-3a19e6af061d&hide_share=true&hideEmbedTopBar=true&hide_title=true&hide_owner=true" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen />

## 1. フレーム開発の準備

[Packages](/tutorials/setup/packages#1) の手順でベースとなるフレームの準備をしましょう。<br />
今回はサンプルフレームのソースが不要ですので、以下のメッセージが表示された際には、**next** を選択して **Enter** キーを押しましょう。

```
Choose a template for the project
```

## 2. コンパイルオプションの編集

この後のセクションでファイルをインポートする必要があり、その際にコンパイルオプションの設定を変更した方がインポートしやすいため、`tsconfig.json` の `compilerOptions` に `baseUrl` の設定追加し、値を以下のように記述します。

```json /tsconfig.json {5} theme={null}
{
  "compilerOptions": {
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "baseUrl": ".",
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next",
      },
    ],
    "paths": {
      "@/*": ["./*"],
    },
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"],
}
```

## 3. フレームで表示する画像の配置

今回作るのはボタンをクリックして画像を切り替えるフレームのため、切り替え前の最初の画像と切り替え後の画像の２つの画像を用意し、`/public` ディレクトリにそれぞれのファイルを配置します。

<Note>
  このレッスンでは、最初のページ用の画像のファイル名を **01.png**、次のページ用の画像のファイル名を **02.png** とします。
</Note>

<Warning>フレームで使用する画像の比率は **1.91:1** または **1:1** である必要があります。<br />
**frames.js** のデフォルトの比率は **1.91:1** で、サイズは **横 1146px x 縦 600px** です。</Warning>

## 4. 不要なソースの削除

タイムラインに表示されるフレームのコアな部分は、`/app/frames/route.tsx` に記述されているため、このファイルをもとにフレームのプログラミングを進めていきます。

ファイルを開くとファイルの中に以下のようなプログラムが記述されているはずですので、まずはプログラムの内容を確認してください。

```typescript /app/frames/route.tsx theme={null}
import { farcasterHubContext } from "frames.js/middleware";
import { createFrames, Button } from "frames.js/next";

const frames = createFrames({
  basePath: '/frames',
  middleware: [
    farcasterHubContext({
      // remove if you aren't using @frames.js/debugger or you just don't want to use the debugger hub
      ...(process.env.NODE_ENV === "production"
        ? {}
        : {
            hubHttpUrl: "http://localhost:3010/hub",
          }),
    }),
  ],
});

const handleRequest = frames(async (ctx) => {
  return {
    image: ctx.message ? (
      <div
        style={{
          display: "flex",
          flexDirection: "column",
        }}
      >
        GM, {ctx.message.requesterUserData?.displayName}! Your FID is{" "}
        {ctx.message.requesterFid}
        {", "}
        {ctx.message.requesterFid < 20_000
          ? "you're OG!"
          : "welcome to the Farcaster!"}
      </div>
    ) : (
      <div
        style={{
          display: "flex",
          flexDirection: "column",
        }}
      >
        Say GM
      </div>
    ),
    buttons: !ctx.url.searchParams.has("saidGm")
      ? [
          <Button action="post" target={{ query: { saidGm: true } }}>
            Say GM
          </Button>,
        ]
      : [],
  };
});

export const GET = handleRequest;
export const POST = handleRequest;
```

内容が確認出来たら以下のように今回修正する部分のソースを以下のように一旦削除します。

```typescript /app/frames/route.tsx {20-21} theme={null}
import { farcasterHubContext } from "frames.js/middleware";
import { createFrames, Button } from "frames.js/next";

const frames = createFrames({
  basePath: '/frames',
  middleware: [
    farcasterHubContext({
      // remove if you aren't using @frames.js/debugger or you just don't want to use the debugger hub
      ...(process.env.NODE_ENV === "production"
        ? {}
        : {
            hubHttpUrl: "http://localhost:3010/hub",
          }),
    }),
  ],
});

const handleRequest = frames(async (ctx) => {
  return {
    image: ,
    buttons: [],
  };
});

export const GET = handleRequest;
export const POST = handleRequest;
```

## 5. カスタム関数の作成

次に `app` ディレクトリ直下に `utils.ts` という名前でファイルを作成します。作成したファイルをエディタで開き、以下の内容をコピー＆ペーストしてファイルを保存します。

```typescript /app/utils.tsx theme={null}
export function appURL() {
  if (process.env.APP_URL) {
    return process.env.APP_URL;
  } else {
    const url = process.env.APP_URL || vercelURL() || "http://localhost:3000";
    console.warn(
      `Warning (examples): APP_URL environment variable is not set. Falling back to ${url}.`
    );
    return url;
  }
}

export function vercelURL() {
  return process.env.VERCEL_URL
    ? `https://${process.env.VERCEL_URL}`
    : undefined;
}
```

## 6. カスタム関数のインポート

次にフレームのプログラムで先ほど作成した `appURL` 関数が使用できるようにするため、`/app/frames/route.tsx` のソースの冒頭部分でファイルをインポートしている箇所に続けて以下のように `appURL` をインポートするため記述を追記します。

```typescript /app/frames/route.tsx {3} theme={null}
import { farcasterHubContext } from "frames.js/middleware";
import { createFrames, Button } from "frames.js/next";
import { appURL } from "app/utils";
```

## 7. プログラムソースの修正

**appURL** 関数をインポートして使える状態にしたら、`image:` にフレームを表示した際に最初に表示させたい画像までのパスを記述し、 `buttons:` に次のページへのボタンとして `<Button action="post" target="/next">次のページへ</Button>` を記述し保存します。

```typescript /app/frames/route.tsx {21,23-25} theme={null}
import { farcasterHubContext } from "frames.js/middleware";
import { createFrames, Button } from "frames.js/next";
import { appURL } from "app/utils";

const frames = createFrames({
  basePath: '/frames',
  middleware: [
    farcasterHubContext({
      // remove if you aren't using @frames.js/debugger or you just don't want to use the debugger hub
      ...(process.env.NODE_ENV === "production"
        ? {}
        : {
            hubHttpUrl: "http://localhost:3010/hub",
          }),
    }),
  ],
});

const handleRequest = frames(async (ctx) => {
  return {
    image: `${appURL()}/01.png`,
    buttons: [
      <Button action="post" target="/next">
        次のページへ
      </Button>
    ],
  };
});

export const GET = handleRequest;
export const POST = handleRequest;
```

<Accordion title="用意した画像が正方形の場合">
  以下のようにソースに `imageOptions` の設定を追記する必要があります。

  ```typescript /app/frames/route.tsx {22-24} theme={null}
  import { farcasterHubContext } from "frames.js/middleware";
  import { createFrames, Button } from "frames.js/next";
  import { appURL } from "app/utils";

  const frames = createFrames({
    basePath: '/frames',
    middleware: [
      farcasterHubContext({
        // remove if you aren't using @frames.js/debugger or you just don't want to use the debugger hub
        ...(process.env.NODE_ENV === "production"
          ? {}
          : {
              hubHttpUrl: "http://localhost:3010/hub",
            }),
      }),
    ],
  });

  const handleRequest = frames(async (ctx) => {
    return {
      image: `${appURL()}/01.png`,
      imageOptions: {
        aspectRatio: "1:1",
      },
      buttons: [
        <Button action="post" target="/next">
          次のページへ
        </Button>
      ],
    };
  });

  export const GET = handleRequest;
  export const POST = handleRequest;
  ```
</Accordion>

## 8. プログラムソースの追加

次に、先ほど `/app/frames/route.tsx` で記述した次のページへ遷移するボタンですが、ボタンをクリックした際の遷移先である `/next` ページに該当するファイルがまだ存在しないため、次のページのプログラムのソースファイルを作成します。

次のページの `next` で表示するフレームのソースファイルは `/app/frames/next/route.tsx` である必要があるため、まずは `next` というディレクトリを作成します。

その中に `/app/frames/route.tsx` をコピーしてペースト、または `route.tsx` というファイルを作成し、`/app/frames/route.tsx` の中身をコピー＆ペーストします。

次のページのファイルを作成ができたら、`image:` に記述された画像のファイル名を次のページに表示したい画像のファイル名に書き換えます。同じように `buttons:` の `target` の文字列を最初のページに戻るためのパス `/` に修正し、ボタンのラベル（`<Button></Button>`で囲われた文字列） を任意の文字列に書き換えて保存します。

```typescript /app/frames/next/route.tsx {21,23-25} theme={null}
import { farcasterHubContext } from "frames.js/middleware";
import { createFrames, Button } from "frames.js/next";
import { appURL } from "app/utils";

const frames = createFrames({
  basePath: '/frames',
  middleware: [
    farcasterHubContext({
      // remove if you aren't using @frames.js/debugger or you just don't want to use the debugger hub
      ...(process.env.NODE_ENV === "production"
        ? {}
        : {
            hubHttpUrl: "http://localhost:3010/hub",
          }),
    }),
  ],
});

const handleRequest = frames(async (ctx) => {
  return {
    image: `${appURL()}/02.png`,
    buttons: [
      <Button action="post" target="/">
        最初のページへ
      </Button>
    ],
  };
});

export const GET = handleRequest;
export const POST = handleRequest;
```

## 9. 作成したフレームの動作確認

以上の一連の作業が完了したら `npm run dev` でローカルサーバーを起動して作成したフレームをデバッガーで確認してみましょう！

## 10. 最終的なソースコード

<Card title="Source Code" icon="github" href="https://github.com/0xfrk/lesson-01" horizontal>
  GitHub Repository: Lesson 1
</Card>
