はじめに
Metaps Advent Calendar 第25日目の記事です。
(弊社の場合、Advent Calendarの投稿日は参加してるエンジニア同士で早い者勝ちで決まるのですが、もたもたしてたら最終日のクリスマス当日枠になってしまいました🎄)
本題に入りますが、Figmaで作成されたデザインからReactコンポーネントを自動生成できれば便利ですよね。
今回は、ずっと気になっていた「Amplify Studio」と「Figma」を同期してReactコンポーネントを自動生成する方法を試してみました。FigmaのデザインからReactコンポーネントに変換してReactアプリに組み込む手順について主に書いていこうと思います。このハンズオンに沿って進めていきます。
事前準備
- Githubアカウント
- AWSアカウント
- Figmaアカウント(無料版でOK)
Amplifyアプリケーションのデプロイ
バックエンドとフロントエンドを1つのGUI開発環境でビルドするために、まずはコチラからGitHubのサンプルリポジトリをフォークして、構成済みのバックエンドが含まれた新しいAmplifyアプリケーションをデプロイします。
Githubに接続したら以下のような画面が表示されるので、好きな名前をつけていきます。
今回はamplify-homesというアプリ名で生成しました。
ここでサービスロールを作成する必要があるので、「新しいロールを作成」を押してIAMからロールを作成していきます。

ロール作成ができたら、そのロールを選択して「保存してデプロイ」をします。

この画面にたどり着くのに5分以上はかかるので、次の手順などを読みながら待つのが良さそうです。

デプロイが完了したら、Backend environmentsタブに移動して、「Studio を起動する」を押します。

Amplify Studio Consoleでシードデータの作成
別タブで以下のAmplify Studioコンソールが起動します。ここからFigmaからインポートしたUI Libraryの設定やシードデータの生成などが操作できます。
早速、サイドメニューのDataからData Modelingの画面をひらいてシードデータを作成していきます。今回は、ID, address, image_url, priceのフィールドを持つデータを定義していきます。
Actions > Auto-generate dataからデータを作成すると、データをいくつ作成するかや、数字の範囲、値の設定などが選択できます。
addressフィールドには住所を入れたいので、「Street address」を設定します。
image_url はUnsplash's random photo generatorを使いましょうとのことなので、それに従って生成された画像のURLを入力していきます。
そのほか、データ自動生成時に何かしらの制約を追加したい場合は Add constrainから追加が可能です。
Generate dataを押してデータの生成が完了!!
ここで生成したデータをUI構築時に使用できるようになります。
Figma でデザインテンプレートをインポート
自分でデザインを生成しても問題ないですが、せっかくなのでAWS Amplify UI Kitテンプレートを使ってみることにします。
このUI Kit でFigma を開いて、まずはREADMEに目を通してみます。

Getting Startedの章でPrimitivesの中身は触るな!!と言われているので、このpageは眺めるだけにしましょう。
ここはAmplify UIでpre-buildされたprimitivesが含まれている箇所なので、このページでデザインに変更を加えたとしても、Amplify Studioに反映されないと言われてますね。(いじくる前にREADME見ていて良かった..)
自分でデザインを作成する場合は、My Components page なら大丈夫そうですね!
primitives page を少し覗いてみると、色んなボタンはもちろんSearchFieldとかSliderFieldなども用意されてました。
Figma でデザインを作成
Figma でデザインの作成をしたことないので、見よう見真似でチュートリアルで紹介されていたデザインを作ってみることにします。 READMEでコンポーネントを作成するにはYOU HAVE TO USE FRAMES と記載されているので、frameをまずは作成していきます。
※ Figma上で自分のアカウントの権限がRead OnlyになっていてFrame作成のボタンが表示されなかったので注意
HomeCardという名前でコンポーネント化し、その中にさらにframeを入れて、TitleとDescriptionを作成していきます。
Figmaの基本的な操作方法を調べながらできたデザインがコチラ。
Figma と Amplify Studio を同期
Figmaで作成したコンポーネントをAmplify Studioに取り込むために同期の設定をしていきます。まずはFigmaの右上のShareボタンを押して、URLをコピーします。
Amplify Studio に戻り、サイドメニューのUI Libraryから右上のSync with Figmaを押し、コピーしたFigmaのURLをAmplify Studio に貼り付けて同期させます。
コピーしたURLを貼り付けると、AmplifyがFigmaへのアクセス権を求めるウィンドウが立ち上がるので、許可をします。

Amplify Studio のUI LibraryにFigmaのデザインがComponentsとして作成されていれば成功です!自作したHomeCardコンポーネントもAmplify Studioに取り込まれてるのが下記の画像から確認できますね。
UI コンポーネントをデータと紐づける
UI LibraryにFigmaでインポートしたデザインがComponentsとして表示されるので、今回自分で作成したHomeCardコンポーネントを選択してConfigureを押します。
ここで開いたUIコンポーネントエディターで、コンポーネントのプロパティと、それに紐づくコンポーネントのUI要素を紐づけていきます。
Component propertiesにはAdd propから入力フィールドを追加し、Data Modeling画面で作成したHomeをTypeに指定します。 Child propertiesにはUIコンポーネントの各要素に紐付けたいデータを指定していきます。
Elements treeで操作したい要素を選択して、どのデータを紐づけるかを直感的に操作することができました。
今回は、要素のTitleにはhome.address、Descriptionにはhome.priceを文字列の中で変数を展開して表示するように割り当てます。 また、onClick actionも割り当てができるので、画像を押したら画像URLに遷移するような動作も実現することができそうです。
これでコンポーネントにデータの紐付けは完了です!
Collectionのコンポーネントを作成する
実際にUIを構築するときには、このコンポーネントを利用して複数のHomeCardを表示させたいので、コレクションを作成してみます。上記キャプチャの右上のCreate collectionからコレクション名を入力して生成されたコンポーネントが以下になります。 これでシードデータを使用したHomeCardCollectionコンポーネントの作成ができました。画面左側のLayoutで確認できますが、Grid表示でのカラム数の指定、X-aligin, Y-align やHomeCardを並べるときの余白もpxで指定することが可能です。
Reactアプリにコンポーネントを取り込む
上記のComponent Editor 画面で</>Get component codeを押すと、Reactアプリに取り込む手順が表示されるので、それに従えばコンポーネントを取り込むことができます。
今回はReactアプリをまだ作成してないので、以下のコマンドを実行してamplify-homesという名前でReactアプリを作成します。
npx create-react-app amplify-homes
次にnpmコマンドを実行します。
npm install -g @aws-amplify/cli
npm install aws-amplify @aws-amplify/ui-react
aws-amplify/ui-reactは、AWS AmplifyのReact用UIコンポーネントライブラリです。このライブラリには、認証コンポーネント(ログイン、サインアップフォームなど)や、様々なUI要素が含まれており、Reactアプリケーションでの使用に最適化されているようです。
次に以下のコマンドを実行します。
amplify pull --appId {YOUR_APP_ID} --envName devh
実行すると、Amplifyにログインされるかを聞かれ、その後、FigmaのコンポーネントをReactのコンポーネントに変換するためにいくつか質問が表示されるので、回答していきます。
すると、コンポーネントが自動生成されました!!
amplify-homesのプロジェクトを確認すると、amplify, /src/ui-componentsディレクトリが追加されてます。そして、ui-componentsの配下には、Amplify Studio から実際にPullしてきたコンポーネントの一覧が格納されてます。Amplify Studio のUI Library > Componentsに存在するコンポーネント名と一致するファイルが生成されてることが確認できます。フロント実装時には、ここからコンポーネントをインポートして使用する形になります。
次にindex.jsを修正します。
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
+ import { ThemeProvider } from "@aws-amplify/ui-react";
+ import { Amplify } from "aws-amplify";
+ import awsconfig from "./aws-exports";
+ import "@aws-amplify/ui-react/styles.css";
+ import { studioTheme } from "./ui-components";
+ Amplify.configure(awsconfig);
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
+ <ThemeProvider theme={studioTheme}>
<App />
+ </ThemeProvider>
</React.StrictMode>
);
reportWebVitals();
App.jsで出力されたコンポーネントをインポートします。テンプレートに存在していたNavBarHeader2もインポートしてみました。Amplify UI の「Flex」コンポーネントで使用できるプロパティを使用できるようです。 この例では試しに、NavBarHeader2にwidth={"100vw"}を指定してみました。
import "./App.css";
import { HomeCardCollection, NavBarHeader2 } from "./ui-components";
function App() {
return (
<div className="App">
<NavBarHeader2 width={"100vw"} />
<HomeCardCollection />
</div>
);
}
export default App;
HomeCardCollectionにisPaginatedプロパティとitemsPerPage={3}などを指定することで、ページネーションを表示できたり1ページに表示させるHomeCardの数などを指定することが可能です。
<HomeCardCollection isPaginated itemsPerPage={3}/>
アプリ起動!!
npm start
FigmaでデザインしたものにAmplify Studio で生成したデータが紐づいた形でUIが構築できてますね! イイ感じです!
まとめ
今回、特に詰まった点はなく、GUIからぽちぽちするだけで、Reactコンポーネントを自動生成できる点は非常に便利と感じました。デザインからコンポーネントを作る作業が爆速になりそうな予感がしました!!ただ実際にFigmaでデザインしてReactの自動生成をしてみた結果、Figmaの知識が少し必要かも、、という所感です。プロジェクトに導入するなら、Figmaでデザイン生成時点でデザイナーと開発者の間でしっかり決め事を決めておかないと想定外のことも起きそうな気がしました。ネックになりそうなポイントを洗い出して検討する必要はありそうかもという感じですね。
最後に出力されたコードを貼り付けて貼り付けておわりにしたいと思います。
Figmaから自動生成されたコンポーネントのjsxファイルのコード
/***************************************************************************
* The contents of this file were generated with Amplify Studio. *
* Please refrain from making any modifications to this file. *
* Any changes to this file will be overwritten when running amplify pull. *
**************************************************************************/
/* eslint-disable */
import * as React from "react";
import { getOverrideProps } from "./utils";
import { Image, Text, View } from "@aws-amplify/ui-react";
export default function HomeCard(props) {
const { home, overrides, ...rest } = props;
return (
<View
width="320px"
height="235px"
display="block"
gap="unset"
alignItems="unset"
justifyContent="unset"
overflow="hidden"
position="relative"
padding="0px 0px 0px 0px"
backgroundColor="rgba(255,255,255,1)"
{...getOverrideProps(overrides, "HomeCard")}
{...rest}
>
<Image
width="320px"
height="160px"
display="block"
gap="unset"
alignItems="unset"
justifyContent="unset"
position="absolute"
top="0px"
left="0px"
padding="0px 0px 0px 0px"
objectFit="cover"
src={home?.image_url}
{...getOverrideProps(overrides, "image")}
></Image>
<View
width="320px"
height="67px"
display="block"
gap="unset"
alignItems="unset"
justifyContent="unset"
overflow="hidden"
position="absolute"
top="168px"
left="0px"
padding="0px 0px 0px 0px"
backgroundColor="rgba(255,255,255,1)"
{...getOverrideProps(overrides, "Frame 437")}
>
<Text
fontFamily="Inter"
fontSize="16px"
fontWeight="700"
color="rgba(0,0,0,1)"
lineHeight="24px"
textAlign="left"
display="block"
direction="column"
justifyContent="unset"
width="320px"
height="25px"
gap="unset"
alignItems="unset"
position="absolute"
top="0px"
left="0px"
padding="0px 0px 0px 0px"
whiteSpace="pre-wrap"
children={home?.address}
{...getOverrideProps(overrides, "Title")}
></Text>
<Text
fontFamily="Inter"
fontSize="12px"
fontWeight="400"
color="rgba(0,0,0,1)"
lineHeight="24px"
textAlign="left"
display="block"
direction="column"
justifyContent="unset"
width="320px"
height="25px"
gap="unset"
alignItems="unset"
position="absolute"
top="33px"
left="0px"
padding="0px 0px 0px 0px"
whiteSpace="pre-wrap"
children={`${"Price: $"}${home?.price}${"/night"}`}
{...getOverrideProps(overrides, "Description")}
></Text>
</View>
</View>
);
}
/***************************************************************************
* The contents of this file were generated with Amplify Studio. *
* Please refrain from making any modifications to this file. *
* Any changes to this file will be overwritten when running amplify pull. *
**************************************************************************/
/* eslint-disable */
import * as React from "react";
import { Home } from "../models";
import { SortDirection } from "@aws-amplify/datastore";
import { getOverrideProps, useDataStoreBinding } from "./utils";
import HomeCard from "./HomeCard";
import { Collection } from "@aws-amplify/ui-react";
export default function HomeCardCollection(props) {
const { items: itemsProp, overrideItems, overrides, ...rest } = props;
const itemsPagination = {
sort: (s) => s.createdAt(SortDirection.DESCENDING),
};
const [items, setItems] = React.useState(undefined);
const itemsDataStore = useDataStoreBinding({
type: "collection",
model: Home,
pagination: itemsPagination,
}).items;
React.useEffect(() => {
if (itemsProp !== undefined) {
setItems(itemsProp);
return;
}
setItems(itemsDataStore);
}, [itemsProp, itemsDataStore]);
return (
<Collection
type="grid"
searchPlaceholder="Search..."
templateColumns="1fr 1fr 1fr"
autoFlow="row"
alignItems="stretch"
justifyContent="stretch"
items={items || []}
{...getOverrideProps(overrides, "HomeCardCollection")}
{...rest}
>
{(item, index) => (
<HomeCard
home={item}
key={item.id}
{...(overrideItems && overrideItems({ item, index }))}
></HomeCard>
)}
</Collection>
);
}






















