Figma MCP × GitHub CopilotでReactコンポーネントを自動生成してみた
はじめに
近年生成AIの利用により、要件を入力することでデザインやコードの生成が高速に行われるようになってきました。ですが、すでに作成されたデザインを忠実にコードに起こすことは一般的な生成AIではまだ苦手な様に感じます。
そこで今回は、Figma REST APIと MCP(Model Context Protocol) を活用して、デザイン情報をJSON形式で直接取得し、それをGitHub Copilotにインプットすることで、デザインに忠実なコンポーネントコードの自動生成を試してみました。
実行環境
Figma APIを実行するにあたって以下の準備が必要です。
Personal Access Tokenの発行
- Figma左上のアイコンからSettingsを選択。
- Securityタブを選択。
- Personal access tokensのGenerate new tokenを選択。
- 名前と有効期限を指定し、スコープとしてfile_content:read(取得用の権限)を付与してトークンを発行します。
- 発行すると画面上にトークンが表示されるので表示されたトークンを取得する(表示後同じものをWeb上から再取得できないため注意が必要)。
ファイルキー(FILE_KEY)の取得
- Figmaのデザインテンプレートを取得
- 今回はフリーで使用できるコミュニティのテンプレートを自分のシートで利用します。
- Draftに保存されたテンプレートファイルからfile_keyを取得
- URLは
https://www.figma.com/design/:file_key/:file_nameの形式となっているので:file_key部分を抜き出して利用します。
- URLは
実行
Figma MCPを利用して、デザインデータをJSONとして保存する処理を実装します。以下は処理の一部抜粋です。
server_params = StdioServerParameters(
command="docker",
args=[
"run",
"-i",
"--rm",
"-e", f"HTTPS_PROXY={https_proxy}",
"-e", f"FIGMA_API_KEY={Config.FIGMA_TOKEN}",
"-e", f"FIGMA_FILE_KEY={Config.FIGMA_FILE_KEY}",
Config.DOCKER_IMAGE,
],
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
args = {
# ここに前述で取得したfileKeyを設定
"fileKey": Config.FIGMA_FILE_KEY
}
res = await session.call_tool(
"get_figma_data",
arguments=args,
)
text = res.content[0].text
os.makedirs("output", exist_ok=True)
data = json.loads(text)
with open("output/figma_data.json", "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
取得したJSON(一部抜粋)は以下のようになります。テキストの色やサイズ、構造が詳細に記述されていることがわかります。
{
"document": {
"id": "0:0",
"name": "Document",
"type": "DOCUMENT",
"scrollBehavior": "SCROLLS",
"children": [
{
"id": "1:2",
"name": "Style Guide",
"type": "CANVAS",
"scrollBehavior": "SCROLLS",
"children": [
{
"id": "1:15",
"name": "Colors",
"type": "FRAME",
"scrollBehavior": "SCROLLS",
"children": [
{
"id": "1:18",
"name": "Primary",
"type": "TEXT",
"scrollBehavior": "SCROLLS",
"blendMode": "PASS_THROUGH",
"fills": [
{
"blendMode": "NORMAL",
"type": "SOLID",
"color": {
"r": 0.0235294122248888,
"g": 0.4431372582912445,
"b": 0.8784313797950745,
"a": 1.0
}
}
],
・・・
取得したJSONはデータ量が膨大なため、AIが処理しやすいようコンポーネント単位のファイルに分割します。
with open(INPUT_PATH, "r", encoding="utf-8") as f:
data = json.load(f)
components = data["document"]["children"][0]["children"]
name_count = {}
for comp in components:
name = comp.get("name", "unnamed")
file_name = f"{name}.json"
out_path = os.path.join(OUTPUT_DIR, file_name)
with open(out_path, "w", encoding="utf-8") as out_f:
json.dump(comp, out_f, ensure_ascii=False, indent=2)
分割したJSONファイルをプロンプトとともにGitHub Copilotなどの生成AIに渡します。
今回はTypographyのコンポーネントを利用し、HeadlinesとBodyの情報を生成します。

# Typography Components Generation Prompt (Atomic Design / Co-location)
`output/components/Typography.json` の定義に基づき、Atomic Designの「Atoms」として、再利用可能な Typography コンポーネント群を作成してください。
## 1. ディレクトリ構成(Co-locationルール)
各コンポーネントは独立したディレクトリに配置し、ロジックとスタイルを物理的に分離して構成してください。
構成イメージ:
`src/components/atoms/Typography/[ComponentName]/`
├── `[ComponentName].tsx`
├── `[ComponentName].module.css`
└── `index.ts`
## 2. 実装対象のコンポーネント(計12個)
以下の各スタイルを、独立したコンポーネントとして作成してください。
- Headlines: `Headline1`, `Headline2`, `Headline3`, `Headline4`
- Body Regular: `Body1Regular`, `Body2Regular`, `Body3Regular`, `Body4Regular`
- Body Medium: `Body1Medium`, `Body2Medium`, `Body3Medium`, `Body4Medium`
## 3. スタイル定義(CSS Modules)
各コンポーネントに対応する `.module.css` を作成し、JSONから以下の数値を正確に抽出してください。
- Size: Headline(64, 36, 28, 20px), Body(18, 16, 14, 12px)
- Weight: Regular(400), Medium(500) を `fontWeight` に反映。
- Detail: `letterSpacing`, `lineHeightPx` を正確に反映。
- Color: 文字色は `var(--color-text-primary, #212121)` を使用。
## 4. コンポーネント要件(React/TypeScript)
- BaseTextの抽出: 共通ロジック(ポリモーフィックな `as` プロップス、className結合)を受け持つ `InternalBaseText` コンポーネントを内部的に作成し、各Atomで利用すること。
- Polymorphic: `as` プロップスにより、HTMLタグ(h1-h6, p, span等)を動的に変更可能にすること。
- デフォルトタグ:Headline系は `h1`〜`h4`、Body系は `p`。
- Flexibility: `clsx` または `tailwind-merge` を使用し、外部からの `className` 拡張を許容すること。
- 型安全: `React.ComponentPropsWithRef` を継承し、`ref` や標準的なHTML属性をサポートすること。
## 5. 出力形式
1. 各ディレクトリごとの `.tsx` と `.module.css` のコード
2. これらを外部から利用しやすくするための `index.ts` (Barrel file)
実行結果
AIによって生成されたReactコードの一例が以下になります。
基幹コンポーネントであるInternalBaseTextと意匠コンポーネントであるBody1Regularに分けて生成されています。
import React from "react";
import { twMerge } from "tailwind-merge";
import clsx from "clsx";
// 型安全なポリモーフィックコンポーネント型定義
export type InternalBaseTextProps<C extends React.ElementType> = {
as?: C;
className?: string;
children: React.ReactNode;
} & Omit<React.ComponentPropsWithRef<C>, "as" | "className" | "children">;
export const InternalBaseText = React.forwardRef(
<C extends React.ElementType = "span">(
{ as, className, children, ...rest }: InternalBaseTextProps<C>,
ref: React.Ref<any>
) => {
const Component = as || "span";
return (
<Component ref={ref} className={className} {...rest}>
{children}
</Component>
);
}
);
InternalBaseText.displayName = "InternalBaseText";
import { InternalBaseText } from "./InternalBaseText";
import { twMerge } from "tailwind-merge";
import clsx from "clsx";
import React from "react";
const STYLES = twMerge(
"text-[18px] font-[400] leading-[28px] tracking-[0em]",
"text-[#212121] text-[var(--color-text-primary),#212121]"
);
export type Body1RegularProps<C extends React.ElementType = "p"> = {
as?: C;
className?: string;
children: React.ReactNode;
} & Omit<React.ComponentPropsWithRef<C>, "as" | "className" | "children">;
export const Body1Regular = React.forwardRef(
<C extends React.ElementType = "p">(
{ as = "p", className, children, ...rest }: Body1RegularProps<C>,
ref: React.Ref<any>
) => (
<InternalBaseText
as={as}
ref={ref}
className={twMerge(clsx(STYLES, className))}
{...rest}
>
{children}
</InternalBaseText>
)
);
Body1Regular.displayName = "Body1Regular";
生成されたReactコードを基に画面を構成した結果、以下のようになりました。

まとめ
Figma MCPを利用してデザインデータを構造化データ(JSON)として取り出すことで、従来の見た目から推測するAI生成とは異なり、定義に基づいた正確なコード化が可能になりました。
今回はTypographyという比較的シンプルなコンポーネントで検証しました。
設計データそのものをAIにインプットしているため、プロンプトやコンポーネントの設計次第で、より複雑なUIや動的なレイアウトにも十分対応できるポテンシャルを感じます。
Discussion