Skip to main content
今回のレッスンで作成する最終的なソースコードを確認したい場合は、ページの最後の 最終的なソースコード から GitHub リポジトリにアクセスしてソースコードをダウンロードしてください。

1. フレーム開発の準備

Packages の手順でベースとなるフレームの準備をしましょう。
今回はサンプルフレームのソースが不要ですので、以下のメッセージが表示された際には、next を選択して Enter キーを押しましょう。
Choose a template for the project

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

この後のセクションでファイルをインポートする必要があり、その際にコンパイルオプションの設定を変更した方がインポートしやすいため、tsconfig.jsoncompilerOptionsbaseUrl の設定追加し、値を以下のように記述します。
/tsconfig.json
{
  "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. フレームで表示する画像の配置

今回作るのはボタンをクリックして画像を切り替えるフレームのため、切り替え前の最初の画像と切り替え後の画像の2つの画像を用意し、/public ディレクトリにそれぞれのファイルを配置します。
このレッスンでは、最初のページ用の画像のファイル名を 01.png、次のページ用の画像のファイル名を 02.png とします。
フレームで使用する画像の比率は 1.91:1 または 1:1 である必要があります。
frames.js のデフォルトの比率は 1.91:1 で、サイズは 横 1146px x 縦 600px です。

4. 不要なソースの削除

タイムラインに表示されるフレームのコアな部分は、/app/frames/route.tsx に記述されているため、このファイルをもとにフレームのプログラミングを進めていきます。 ファイルを開くとファイルの中に以下のようなプログラムが記述されているはずですので、まずはプログラムの内容を確認してください。
/app/frames/route.tsx
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;
内容が確認出来たら以下のように今回修正する部分のソースを以下のように一旦削除します。
/app/frames/route.tsx
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 という名前でファイルを作成します。作成したファイルをエディタで開き、以下の内容をコピー&ペーストしてファイルを保存します。
/app/utils.tsx
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 をインポートするため記述を追記します。
/app/frames/route.tsx
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> を記述し保存します。
/app/frames/route.tsx
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;
以下のようにソースに imageOptions の設定を追記する必要があります。
/app/frames/route.tsx
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;

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>で囲われた文字列) を任意の文字列に書き換えて保存します。
/app/frames/next/route.tsx
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. 最終的なソースコード

Source Code

GitHub Repository: Lesson 1