Next.jsでv0を使ってみる。


## ユーザーインタフェースのデザイン

オリジナルのWebアプリケーションをなにか作ろうと考えたときに、悩みの種がユーザーインタフェースをどうするかですね。私はデザインセンスがないので、HTMLやCSSを時間かけて頑張って書いても、なんだか微妙だなというものしかできません。さてどうしたものか…。

## V0

そんな悩みを持った方向けに、Vercelが新たなサービスを始めようとしています。それが`V0`です。`V0`ってなにかと言うと、最近よく話題になる生成系AI(ChatGPT等)にユーザーインタフェースを考えてもらって、ソースコードも生成してもらうサービスです。
生成されるソースコードは`shadcn/ui`と`Tailwind CSS`を使用して作成されたReact系のコンポーネントです。

`shadcn/ui`とは、UIコンポーネント集です。特徴的なのはライブラリではなく、ソースコードをコピペする感じでアプリケーションに適用することです。内部的に`Tailwind CSS`を使っていますが、これらはどちらも実行時に特定のライブラリを必要としないことが特徴です。そういった特徴を持つソフトウェアは`ゼロランタイム`と呼ばれ、昨今のフロントエンド界隈では流行しているようです。ちなみに`shadcn`は"シャッドシーエヌ"と読むらしいです。

参考URL：https://v0.dev/
参考URL：https://ui.shadcn.com/


## v0を使ってみる

それでは`v0`を試してみましょう。
`v0`は現在のところpublic betaとして運営されています。利用するには`waitlist`
に登録する必要があり、すぐに利用できるわけではありません。（とはいえ、`waitlist`に登録してから1週間ほど待ってたら利用可能になりました）

`https://v0.dev/`にアクセスします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24283/8bce7bed-7d39-6d8b-146f-a066b770bc7d.png)

生成系AIでよくあるプロンプト入力のための入力欄が一つだけのシンプルな画面です。
それでは、ここに作ってほしいUIを書いてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24283/666ade5c-2bbf-8903-97b1-2628cac5dadc.png)

すると下記のような三つの候補を出してくれます。

一つ目の候補
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24283/9151768d-4a97-704c-6f97-2d7c84fcc396.png)

二つ目の候補
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24283/e98f8346-0463-3c1a-945a-687b95542d5f.png)


三つ目の候補
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24283/fed54bb8-31d8-453e-2385-2d366e0d616f.png)


私は二つ目が良さそうだと思ったので、これを使います。
プロンプトを追加して変更を行ってもらうこともできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24283/761c783f-26f4-3f74-10ed-f6dba1d54114.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24283/17fc6677-62d3-eaa8-25b8-ff1370e5f50c.png)

## Next.jsアプリケーションへの適用

アプリケーションへ適用するには、v0の右上にある`code</>`ボタンを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24283/7d8920e5-c411-0343-5096-758c2ab2aa83.png)

すると次のような画面に切り替わり、今回生成したUIのソースコードが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24283/97b9433d-0beb-24c8-ed88-82235d25334e.png)

これをこのままコピーして、アプリケーションに適用することもできますが、`shadcn/ui`のコンポーネントは別途準備する必要があります。
そのあたりを一括でやってくれるコマンド`npx v0 add T0QPlTwHTvJ`も上記の画面上に提示されています。

このコマンドをアプリケーション内で実行することで、今回生成したUIのソースコードと、そこで利用されている`shadcn/ui`のコンポーネントを一括でインストールできます。
ただしこのコマンドを実行する前に初期化コマンドを実行しておく必要があります。下記のコマンドをアプリケーション内で実行してください。

```
>cd next-notslack-app
```
↑※`next-notslack-app`はアプリケーションフォルダの名称と仮定しています。

```
> npx v0@latest init

✔ Writing components.json...
✔ Initializing project...
✔ Installing dependencies...

```

それが済んだら、改めて下記のコマンドを実行します。途中でインストールする画面のコンポーネントとしての名前を聞かれますので、わかりやすい名前をつけておきましょう。

```
> npx v0 add T0QPlTwHTvJ

✔ What should we name the component? … Board
✔ Done.
```

これで完了です。

アプリケーションのソースコードを除いてみると、下記のフォルダとファイルが追加されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24283/84c493f3-7205-4e83-6c1c-299aa8b33fcd.png)

`board.tsx`が生成したUIをコンポーネント化したソースコードです。ただし一応、拡張子は `tsx` なのですが、ソースコードの中身はあきらかにJavaScriptです。それを修正して利用するのが本当はいいのでしょうが、ここではメンドーなので、拡張子を`jsx`にしてしまって逃げてしまいます。

```javascript:components/board.jsx(途中の日本語コメントは筆者による)
/**
 * This code was generated by v0 by Vercel.
 * @see https://v0.dev/t/T0QPlTwHTvJ
 */
import Link from "next/link"
// ここから下は`shadcn/ui`のコンポーネントをインポートしている
import { Button } from "@/components/ui/button"
import { CardTitle, CardDescription, CardHeader, CardContent, Card } from "@/components/ui/card"
import { Avatar } from "@/components/ui/avatar"
import { Input } from "@/components/ui/input"

export function Board() {
  return (
  　{/* コンポーネントのスタイルはTailwind CSSを利用している */}     
    <div className="grid min-h-screen w-full lg:grid-cols-[280px_1fr_280px]">
      <div className="hidden border-r bg-gray-100/40 lg:block dark:bg-gray-800/40">
        <div className="flex h-full max-h-screen flex-col gap-2">
          {/* 途中省略 */} 
        </div>
      </div>
    </div>
  )
}

//アイコンもSVGを使ったコンポーネント。
//ただしなぜか引数に型アノテーションが無く、TypeScript的にはアウト!
function EqualNotIcon(props) {
  return (
    <svg
      {...props}
      xmlns="http://www.w3.org/2000/svg"
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      strokeWidth="2"
      strokeLinecap="round"
      strokeLinejoin="round"
    >
      <line x1="5" x2="19" y1="9" y2="9" />
      <line x1="5" x2="19" y1="15" y2="15" />
      <line x1="19" x2="5" y1="5" y2="19" />
    </svg>
  )
}
//以下、省略
```

`ui`フォルダの中には利用している`shadcn/ui`のコンポーネントが配置されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24283/8724cb29-0f9f-2ed4-b046-36012b983dec.png)

`board.jsx`をホーム画面となるコンポーネント`app/page.tsx`に配置すると、とりあえずアプリケーションで表示することができます。

```typescript:app/page.tsx
import { Board } from "@/components/board";

export default function Home() {
  return (
    <Board />
  )
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24283/02219586-3749-0cc9-2468-2f76e164894e.png)

v0が生成した`board.tsx`は、`shadcn/ui`と`Tailwind CSS`を使用して作成されたコンポーネントとして純粋に記述されていますので、ここから手を入れていくのは容易でしょう。