[初心者向け]はじめてのNext.js - Figma×Locofyでソースコードを生成して自分のプロジェクトに適用する

こんにちは。Kaneyasuです。
先日からNext.jsとVercelを始めました。
前回までの分はこちら。

https://qiita.com/satoshi256kbyte/items/6821bd0d4de8aeb46201

https://qiita.com/satoshi256kbyte/items/d95d23c8fb0516e20e04

今回はFigmaからソースコードを生成してみます。
Figmaからコードを出力するにはプラグインの力が必要で、今回はLocofyを使用してみました。
手順を自分のような初心者の方向けに書き残しておくのでよろしければご覧ください。

# Figmaへのサインアップ

Figmaにサインアップします。
PlanはFreeでやってみます。

[Figma Learn - Figmaアカウントの作成](https://help.figma.com/hc/ja/articles/360039811114-Figma%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%81%AE%E4%BD%9C%E6%88%90)

私の場合ログインすると、DraftsにFigma basicsというファイルがあるのでこれを使用します。

![FigmaのDrafts](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1959433/060bdd83-b706-8d71-935b-40c5958ad120.png)

# Dev Modeへの切り替え

ファイルを開くと、右上にDev Modeへ切り替えるスイッチがあるのでこれをクリックします。

<img width="300px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1959433/61e93bb6-66f1-6693-e3fb-15baad7aea3e.png" />
<br>
↓
<br>
<img width="300px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1959433/392f15a8-03b1-43b4-e85c-da524eba7034.png" />

# Locofyプラグインのインストール

Figmaからコードを出力するにはプラグインの力が必要です。
デフォルトで、AnimaとFigma to Codeというプラグインが入っていますが、Animaは本来有料でフリープランだとすぐ上限になり、Figma to Codeは若干機能が弱そうなので、今回はLocofyというものを使ってみます。

Dev Modeに切り替えた後、Pluginsタブをクリックします。

<img width="300px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1959433/d9c29af1-ccb0-f674-c149-6233371e0f7a.png" />

検索ボックスにLocofy.aiと入力して検索。

<img width="300px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1959433/bc6a8034-b908-7501-93e7-2f5dae108914.png" />

クリックすると詳細情報が表示されるので`Run`をクリック。

<img width="300px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1959433/810f2ebb-dca2-6b09-8e91-013dac5ec06c.png" />

アカウントの接続を求められるので、`Connect Account`をクリック。

<img width="300px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1959433/9020e06d-66c2-52a4-0ed0-651370e36c2c.png" />

別タブで、Locofyのサイトが開きます。
Googleアカウントなどでサインアップします。
私はFigmaもLocofyもGoogleアカウントでサインアップしました。
おそらく、サインアップする方法は合わせた方がよいと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1959433/bc1a8ade-4b07-c277-2f91-69e947fe659b.png)

サインアップ後、Figmaに戻ってプラグインでLocofyを開くと、コード変換の設定などが表示されるようになります。

<img width="300px" src="<https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1959433/7e72dafa-a548-ed50-b059-77105e7aa786.png>" />

# LocofyでNext.jsのコードを出力する

プラグインにLocofyをインストール後、ファイルを開いて何らかのページを選択してPluginsのLocofyを開くと、コード変換の設定などが表示されます。

![何らかのページを選択してLocofyを開く](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1959433/151befe5-1633-1726-dc2a-b8600d7a97ae.png)

上部にフレームワークとスタイルの出力方法を選択する欄があるので、Next.jsを選択。
スタイルは今回はTailwindを使用してるのでそれを選択。

<img width="300px" src="<https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1959433/49ec8746-9c0d-5e61-a0dc-8ec9a7c6c5f0.png>" />

<img width="300px" src="<https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1959433/023088f2-554b-2d31-719a-12626b646d48.png>" />

画面下部にダウンロードボタンがあるのでクリックすると、Zip圧縮されたファイル一式がダウンロードされます。
（ヘルプのアイコン邪魔ですね・・・）

<img width="300px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1959433/91c36485-8c78-0d8c-88c8-4077615d4924.png" />

ダウンロードしたファイルを解凍すると以下の構成のファイル群ができます。

```bash
./
├── README.md
├── next-env.d.ts
├── next.config.js
├── package.json
├── pages
│   ├── _app.tsx
│   ├── global.css
│   └── index.tsx
├── postcss.config.js
├── public
│   ├── jonathankemper1hhrdiolfpuunsplash-1@2x.png
│   └── stocksy-txp226f62b2ane300-medium-4582193-1@2x.png
├── tailwind.config.js
├── tsconfig.json
├── typings.d.ts
└── vercel.svg
```

# 既存のNext.jsプロジェクトへのマージ

出力したコードを既存のNext.jsプロジェクトへマージしてみます。
`npx create-next-app`で出力したものと差を比較しながら適用します。

いろいろ試したところ、出力されたファイル一式のうち、○がついてるもの以外はマージする必要はないと思いました。

```bash:Locofyが出力してくれたファイル一式（○がついてるもの以外は不要）
./
├── README.md
├── next-env.d.ts
├── next.config.js
├── package.json <<< ○
├── pages
│   ├── _app.tsx <<< ○
│   ├── global.css <<< ○
│   └── index.tsx <<< ○
├── postcss.config.js
├── public
│   ├── jonathankemper1hhrdiolfpuunsplash-1@2x.png <<< ○
│   └── stocksy-txp226f62b2ane300-medium-4582193-1@2x.png <<< ○
├── tailwind.config.js <<< ○
├── tsconfig.json
├── typings.d.ts
└── vercel.svg
```

## package.jsonにライブラリを追加

autoprefixerが追加されていたので、マージではなく追加で`npm install autoprefixer --save-dev`しました。

https://autoprefixer.github.io/

## 画像とページのコードを自分のプロジェクトに組み込む

### 画像とページのコードを自分のプロジェクトに配置

Locofyが出力した画像を`public`に配置。
次にコードですが、Locofyが出力したコード群には`pages`がありますが、私のプロジェクトには`pages`がないので、`src/app`配下にディレクトリを作って、そこに配置します。

```bash:自分のNext.jsプロジェクト
./
├── public
│   ├── jonathankemper1hhrdiolfpuunsplash-1@2x.png <<< Locofyが出力した画像
│   ├── shiba.png
│   └── stocksy-txp226f62b2ane300-medium-4582193-1@2x.png <<< Locofyが出力した画像
├── src
│   └── app
│       ├── dogs
│       │   └── page.tsx
│       ├── homepage <<< ディレクトリを新規作成、Locofyが出力したコード群のpages一式を移動
│       │   ├── _app.tsx
│       │   ├── global.css
│       │   └── index.tsx
│       ├── favicon.ico
│       ├── globals.css
│       ├── layout.tsx
│       └── page.tsx
├── next-env.d.ts
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── README.md
├── tailwind.config.ts
└── tsconfig.json
```

ここの違いは、私のプロジェクトがNext.js 13だからですね。
Next.js 13からは、ルートディレクトリは`app`に変更されています。

https://nextjs.org/blog/next-13

どうやら、Locofyが出力するコードは、Next.js 13より前のコードのようです。
Locofyが出力した`package.json`には、`"next": "^13.2.4",`とあるので、古いというより、13より前のNext.jsでも動くようにあえて古いスタイルで出力しているように思えます。

### index.tsxをpage.tsxにリネーム

Next.js 13からはインデックスファイルは、`index.tsx`から`page.tsx`に変わっているのでリネーム。

```bash:自分のNext.jsプロジェクト
./
├── public
│   ├── jonathankemper1hhrdiolfpuunsplash-1@2x.png 
│   ├── shiba.png
│   └── stocksy-txp226f62b2ane300-medium-4582193-1@2x.png
├── src
│   └── app
│       ├── dogs
│       │   └── page.tsx
│       ├── homepage
│       │   ├── _app.tsx
│       │   ├── global.css
│       │   └── page.tsx <<< リネーム
│       ├── favicon.ico
│       ├── globals.css
│       ├── layout.tsx
│       └── page.tsx
├── next-env.d.ts
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── README.md
├── tailwind.config.ts
└── tsconfig.json
```

### _app.tsxをlayout.tsxに変更

Next.js 13からはレイアウトは、`_app.tsx`から`layout.tsx`で定義するように変わっているのでこれも変更。
しかし、中身を比較してみたら構造が違います。

```bash:自分のNext.jsプロジェクト
./
├── public
│   ├── jonathankemper1hhrdiolfpuunsplash-1@2x.png 
│   ├── shiba.png
│   └── stocksy-txp226f62b2ane300-medium-4582193-1@2x.png
├── src
│   └── app
│       ├── dogs
│       │   └── page.tsx
│       ├── homepage
│       │   ├── _app.tsx <<< このファイルと(A)を比較
│       │   ├── global.css
│       │   └── page.tsx
│       ├── favicon.ico
│       ├── globals.css
│       ├── layout.tsx <<< (A)
│       └── page.tsx
├── next-env.d.ts
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── README.md
├── tailwind.config.ts
└── tsconfig.json
```

```tsx:./src/app/homepage/_app.tsx
import { Fragment } from "react";
import Head from "next/head";
import type { AppProps } from "next/app";
import "./global.css";

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <Fragment>
      <Head>
        <title>locofy-react-nextjs-project</title>
        <meta
          name="viewport"
          content="minimum-scale=1, initial-scale=1, width=device-width"
        />
      </Head>
      <Component {...pageProps} />
    </Fragment>
  );
}

export default MyApp;
```

```tsx:./src/app/layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "kaneyasu", // ここの文字列がブラウザのタブに表示される
  description: "Generated by kaneyasu", // こちらはメタタグに表示される文字列
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>{children}</body>
    </html>
  );
}
```

単にリネームしただけでは動作しなかったので、(A)をベースに新たに`layout.tsx`を作成

```./src/app/homepage/layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./global.css";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "locofy-react-nextjs-projec", // ここの文字列がブラウザのタブに表示される
  description: "locofy-react-nextjs-projec", // こちらはメタタグに表示される文字列
};

export default function HomepageLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>{children}</body>
    </html>
  );
}
```

```bash:自分のNext.jsプロジェクト
./
├── public
│   ├── jonathankemper1hhrdiolfpuunsplash-1@2x.png 
│   ├── shiba.png
│   └── stocksy-txp226f62b2ane300-medium-4582193-1@2x.png
├── src
│   └── app
│       ├── dogs
│       │   └── page.tsx
│       ├── homepage
│       │   ├── layout.tsx <<< (A)をベースに作成
│       │   ├── global.css
│       │   └── page.tsx
│       ├── favicon.ico
│       ├── globals.css
│       ├── layout.tsx <<< (A)
│       └── page.tsx
├── next-env.d.ts
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── README.md
├── tailwind.config.ts
└── tsconfig.json
```

## tailwind.config.tsをマージ

私のプロジェクトの`tailwind.config.ts`と、Locofyが出力した`tailwind.config.js`を比較します。

```ts:自分のプロジェクトのtailwind.config.ts
import type { Config } from "tailwindcss";

const config: Config = {
  content: [
    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      backgroundImage: {
        "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
        "gradient-conic":
          "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
      },
    },
  },
  plugins: [],
};
export default config;
```

```js:Locofyが出力したtailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {
      colors: {
        white: "#fff",
        darkolivegreen: "#426b1f",
        black: "#000",
      },
      spacing: {},
      fontFamily: {
        inter: "Inter",
        newsreader: "Newsreader",
      },
    },
    fontSize: {
      base: "16px",
      sm: "14px",
      xl: "20px",
      inherit: "inherit",
    },
  },
  corePlugins: {
    preflight: false,
  },
};
```

TypeScriptと JavaScriptの違いはありますが、JavaScriptのオブジェクトリテラル形式なのでマージができました。
contentのパスは、Locofyの方だとディレクトリ構造と合わないので、私のプロジェクトの方を正にしました。
themeと、corePluginsはLocofyの方を正にしました。

```ts:マージしたtailwind.config.ts
import type { Config } from "tailwindcss";

const config: Config = {
  content: [
    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      colors: {
        white: "#fff",
        darkolivegreen: "#426b1f",
        black: "#000",
      },
      spacing: {},
      fontFamily: {
        inter: "Inter",
        newsreader: "Newsreader",
      },
    },
    fontSize: {
      base: "16px",
      sm: "14px",
      xl: "20px",
      inherit: "inherit",
    },
  },
  plugins: [],
  corePlugins: {
    preflight: false,
  },
};
export default config;
```

# 動作確認

マージが済んだら動作確認してみます。

![動作確認結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1959433/6a122630-3aae-57a2-a8ce-a2e5e80164c9.png)

画像の表示だけ違和感を感じますね。
上述のファイル以外にもいろいろ調整してみましたが結果変わらずなので、これが限界なのかなと思います。
それでもゼロから作るよりは大分楽なので有用であることは感じられました。

有料プラグインのAnimaだとまた違う結果が得られるのかもしれません。
機会があったら試してみます。