🎮

public beta になった v0 を試してWebサイトを作る

2024/09/03に公開

v0 とは

v0 は vercel 社が提供する React ベース[1]の UI を作ることができる生成 AI サービスです。
こちらの指示をもとに、 shadcn/ui や tailwind を使って UI を構築することができます。

https://v0.dev/

先日、public Beta 版が公開され、会話形式で UI を逐次生成できるようになりました。

https://x.com/v0/status/1826020673908535325

これまでは、プロンプトを v0 に投げると数パターン実装例を提案してくれていましたが、
ついに会話でやりとりをしながらコンポーネントをブラッシュアップしていくことが出来るようになりました。

早速ログインして試してみます。
できることとしては下記の通りです。(2024年9月1日現在)

  • ジェネレーティブAIを使って、shadcn/uiとTailwind CSSに基づいたコピー&ペーストしやすいReactコードを作成します。
  • プロンプトを送信するか画像をアップロードすると、v0はAIを使って3つの異なるUIを生成します。
  • 生成されたUIの個々のパーツを選択して、作成したものを改良することができます(またはコードを直接編集することもできます)。準備ができたら、コードをコピーしてReactアプリに貼り付け、送信できます!
  • 「生成開始」をクリックするか、この製品を使用することで、利用規約に同意したことになります。本規約は、本規約に定めるとおり、キャンセルしない限り自動的に更新されます。
英語表記
  • Use generative AI to create copy-and-paste friendly React code based on shadcn/ui and Tailwind CSS.
  • When you submit a prompt or an upload an image, v0 will use AI to generate three different UIs
  • You can select individual parts of a generated UI to refine your creation (or edit the code directly). Once you're ready, you can then copy the code, paste it into your React app and ship!
  • By clicking "Start Generating" or using this product, you accept the Terms of Use. This Agreement will renew automatically, unless you cancel, as set forth in the Agreement.

Pricing

フリープランは200クレジット/月ほど使えますが、
月20$のプレミアムプランであれば5000クレジット/月の他、
いくつかのサポートを受けられます。

v0 Subscription & Billing
プレミアムプラン加入例

詳しくは Pricing ページをご確認ください。

v0 と対話しながらサイトを作ってみる

TOP ページの Get Started ボタンから会話形式で生成できるページに遷移できます。

https://v0.dev/chat

わたしは個人開発で好きな漫画のファンサイトを Next.js で作っているのですが、
pages router で動いているので、これを機会に app router 化するのを手伝ってもらいます。

いくつか v0 で体験したことを抜粋してお伝えできたらと思います。

プロンプトと返答例

プロンプトにはどんなサイトを作りたいかと、使いたいライブラリや BaaS などを条件に投げてみました。
試しに、現状でも使っている supabase を使うことと、
Awesome Javascript という、JS のライブラリをまとめたリポジトリから
ドラッグアンドドロップ機能を提供するライブラリを見つけてもらいます。

ちなみに、すでに国際化が対応されており、v0 に前提を共有しなくても日本語でプロンプトを投げたら
日本語で返答してくれました。

👇投げたプロンプト

漫画のファンサイトを作りたいです。 題材は漫画の各話、お酒とファッション、映画、音楽です。
すべて実在するモチーフがあります。
必要な機能を取り揃え、データベースとして活用できるサイトにしたいです。
ページ構成を検討してデモサイトを構築してください。
また、DBとしてsupabaseを使用することを前提とし、
訪れたユーザは自分が飲んだことのあるお酒があったら投票できるような機能をつけたいです。

ユーザは自分好みのお酒のリストを作ることができ、それを公開するページも作成したいです。
リストはドラッグアンドドロップで並び替えることができるようにしたいです。
使用するライブラリは Awesome Javascript に並んでいるものから選択することは出来ますか?

👇返答例の抜粋

(中略)
1. react-beautiful-dnd: ドラッグアンドドロップ機能のために使用します。このライブラリはAwesome JavaScriptのリストに含まれており、Reactアプリケーションで使いやすいドラッグアンドドロップ機能を提供します。
2. @supabase/supabase-js: Supabaseとの連携のために使用します(既に使用していると仮定します)。

それでは、ユーザーのお酒リストページを作成しましょう。
まず、必要なライブラリをインストールします:
(後略)

残念ながら提案された react-beautiful-dnd は少し古く、React 18 以降はサポートされいません。
念のため、React 18 以降との互換性を聞いてみました。

v0 は Strict Mode との互換性についての課題や、一部機能に問題があることを報告し、別のライブラリを提案してくれました。

代替案:
React 18 以降で完全に互換性のあるドラッグアンドドロップライブラリを探している場合、以下の選択肢があります:

- `@hello-pangea/dnd`: react-beautiful-dnd のフォークで、React 18 との互換性問題を解決しています。
- `react-dnd`: より低レベルなAPIを提供しますが、React 18 に対応しています。
- `dnd-kit`: モダンで軽量なドラッグアンドドロップライブラリで、React 18 に完全対応しています。

ChatGPT とは異なり、v0 が生成したファイル名をクリックすると、
右のペインで実際に作られる UI やコードを見ることができます。
その他、 install をクリックするとローカルマシンに作られたプロジェクトを取り込むことができます。

v0が作ったコンポーネントをpreviewしている様子
v0が作ったコンポーネントをpreviewしている様子

app router を依頼したものの、提案されたディレクトリが pages なのが気になりますが、
使えそうなコンポーネントを数秒で提案されました。

v0 が提案する @hello-pangea/dnd を使った実装例
pages/user/drinks-list.tsx
'use client'

import { useState, useEffect } from 'react'
import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Switch } from "@/components/ui/switch"
import { Label } from "@/components/ui/label"

type DrinkItem = {
  id: string
  name: string
  order: number
}

export default function UserDrinksList() {
  const [drinkItems, setDrinkItems] = useState<DrinkItem[]>([
    { id: '1', name: 'ビール', order: 0 },
    { id: '2', name: '日本酒', order: 1 },
    { id: '3', name: 'ワイン', order: 2 },
    { id: '4', name: 'ウイスキー', order: 3 },
    { id: '5', name: 'カクテル', order: 4 },
  ])
  const [isPublic, setIsPublic] = useState(false)

  const onDragEnd = (result) => {
    if (!result.destination) {
      return
    }

    const items = Array.from(drinkItems)
    const [reorderedItem] = items.splice(result.source.index, 1)
    items.splice(result.destination.index, 0, reorderedItem)

    const updatedItems = items.map((item, index) => ({
      ...item,
      order: index,
    }))

    setDrinkItems(updatedItems)
  }

  const togglePublic = () => {
    setIsPublic(!isPublic)
  }

  return (
    <div className="space-y-8">
      <h1 className="text-4xl font-bold text-center">マイお酒リスト</h1>
      <div className="flex items-center space-x-2">
        <Switch id="public-mode" checked={isPublic} onCheckedChange={togglePublic} />
        <Label htmlFor="public-mode">公開する</Label>
      </div>
      <DragDropContext onDragEnd={onDragEnd}>
        <Droppable droppableId="drinks">
          {(provided) => (
            <ul {...provided.droppableProps} ref={provided.innerRef} className="space-y-4">
              {drinkItems.map((item, index) => (
                <Draggable key={item.id} draggableId={item.id} index={index}>
                  {(provided) => (
                    <li ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
                      <Card>
                        <CardHeader>
                          <CardTitle>{item.name}</CardTitle>
                        </CardHeader>
                        <CardContent>
                          <p>順位: {index + 1}</p>
                        </CardContent>
                      </Card>
                    </li>
                  )}
                </Draggable>
              ))}
              {provided.placeholder}
            </ul>
          )}
        </Droppable>
      </DragDropContext>
      <Button onClick={() => console.log('Save list')}>リストを保存</Button>
    </div>
  )
}

そのほか、認証認可も supabase に寄せたいので、
signIn, signOut のあたりを useContext で実装する例も作ってもらいました。

app/contexts/AuthContext.tsx の実装例
app/contexts/AuthContext.tsx
'use client'

import { createContext, useContext, useState, useEffect } from 'react'
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)

const AuthContext = createContext<{
  user: any | null
  signIn: (email: string, password: string) => Promise<void>
  signOut: () => Promise<void>
}>({
  user: null,
  signIn: async () => {},
  signOut: async () => {},
})

export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
  const [user, setUser] = useState<any | null>(null)

  useEffect(() => {
    const { data: authListener } = supabase.auth.onAuthStateChange((event, session) => {
      setUser(session?.user ?? null)
    })

    return () => {
      authListener.subscription.unsubscribe()
    }
  }, [])

  const signIn = async (email: string, password: string) => {
    const { error } = await supabase.auth.signInWithPassword({ email, password })
    if (error) throw error
  }

  const signOut = async () => {
    const { error } = await supabase.auth.signOut()
    if (error) throw error
  }

  return (
    <AuthContext.Provider value={{ user, signIn, signOut }}>
      {children}
    </AuthContext.Provider>
  )
}

export const useAuth = () => useContext(AuthContext)

UI だけでなく、必要な機能もさくっと作ることができたので、必要に応じて調整します。
同じようにリストの並び替えや絞り込み機能などもすぐに作れそうです。

ただ、v0 上で npm パッケージをインストールして動作を確認することはできないので、
実際のところ本当に動くかどうかは Install してローカルマシン上で試すのが良いでしょう。

Install してみる

Install をクリックするとモーダルが出現し、ターミナルにコピペすると取り込めます。

Installボタンをクリックしたときの様子

ターミナルで実行すると、いくつかの質問に答えながら新規で Next.js のプロジェクトが立ち上がります。


ターミナルで実行している様子

Install が完了したのでプロジェクトの中身を確認すると、
app router でプロジェクトが作られていました。すごい。


buttonコンポーネントの実装

ちなみに今回生成されたプロジェクトの package.json はこのような形でした。

package.json
{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@radix-ui/react-slot": "^1.1.0",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.1.1",
    "lucide-react": "^0.438.0",
    "next": "14.2.7",
    "react": "^18",
    "react-dom": "^18",
    "tailwind-merge": "^2.5.2",
    "tailwindcss-animate": "^1.0.7"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "eslint": "^8",
    "eslint-config-next": "14.2.7",
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "typescript": "^5"
  }
}

devDependencies でインストールしているパッケージ群はバージョンの形式が気になりますが、
あとは順次必要なパッケージを追加していけばさっと始められそうです。

ちなみに、Install ボタンの横にある Publish ボタンをクリックすると、作られたコードブロックをそのまま公開することができます。ちなみにチャットの内容は非公開なので、完成されたものだけを見せられます。

Publishボタンをクリックした時の様子

デザインはともかく、コア機能やベーシックな UI はものの数分で生成できるので、
何をサイトの価値として提供したいのか仕様をこちらで決めれば
今までよりも何倍も早くリリース出来そうです。

もちろん、機能要件・非機能要件の壁打ちもできるので、
提案された中から良さそうなものをまとめながら作っていくことができます。

提案された要件

ここまでちゃんとやれたらいいもの出来そうです

ご質問ありがとうございます。漫画ファンサイトを公開するにあたり、検討すべき機能要件と非機能要件を以下にまとめました。それぞれ分けて提案いたします。

機能要件:

1. ユーザー管理
    1. ユーザー登録機能
    2. ログイン/ログアウト機能
    3. パスワードリセット機能
    4. プロフィール編集機能
2. コンテンツ管理
    1. 漫画情報の登録、編集、削除機能
    2. お酒、ファッション、映画、音楽の情報登録、編集、削除機能
    3. コンテンツの検索機能
    4. コンテンツのカテゴリ分類機能
3. ユーザーインタラクション
    1. お気に入りリスト作成機能(お酒、漫画など)
    2. リストの並び替え機能(ドラッグアンドドロップ)
    3. リストの公開/非公開設定機能
    4. コメント投稿機能
    5. 評価/レビュー機能
4. お酒関連機能
    1. ユーザーによる投票機能
    2. 人気ランキング表示機能
5. レコメンデーション
    1. ユーザーの好みに基づいたコンテンツ推奨機能
6. 通知機能
    1. 新着コンテンツや更新情報の通知
7. 管理者機能
    1. コンテンツの承認/拒否機能
    2. ユーザー管理機能(停止、削除など)
    3. サイト統計情報の閲覧機能

非機能要件:
1. パフォーマンス
    1. ページロード時間の最適化(目標:2秒以内)
    2. 大量のデータ処理時の応答性能の確保
    3. CDNの利用によるコンテンツ配信の高速化
2. セキュリティ
    1. HTTPS通信の強制
    2. ユーザー認証とアクセス制御の実装
    3. クロスサイトスクリプティング(XSS)対策
    4. SQLインジェクション対策
    5. CSRF(クロスサイトリクエストフォージェリ)対策
3. 可用性
    1. 99.9%以上のアップタイム
    2. バックアップと災害復旧計画の策定
    3. 負荷分散システムの導入
4. スケーラビリティ
    1. ユーザー数やデータ量の増加に対応できる設計
    2. マイクロサービスアーキテクチャの検討
5. ユーザビリティ
    1. レスポンシブデザインの実装(モバイル対応)
    2. アクセシビリティガイドラインの遵守(WCAG 2.1 AAレベル)
    3. 直感的なUIとナビゲーション設計
6. 互換性
    1. 主要ブラウザ(Chrome、Firefox、Safari、Edge)での動作保証
    2. 過去3年分のブラウザバージョンのサポート
7. 国際化と地域化
    1. 多言語サポート(将来の拡張性を考慮)
    2. 地域ごとのコンテンツカスタマイズ機能
8. コンプライアンス
    1. GDPR、CCPAなどのデータプライバシー法への準拠
    2. コンテンツの著作権管理
9. 保守性
    1. モジュール化されたコード設計
    2. 包括的なドキュメンテーション
    3. 自動化されたテスト(単体テスト、統合テスト、E2Eテスト)
10. 監視とロギング
    1. エラーログの収集と分析
    2. ユーザー行動の追跡と分析(プライバシーに配慮)
    3. パフォーマンスメトリクスの監視

まとめ

x での v0 のポストを追いかけていると日々高速でアップデートされていたり、
様々な開発者が UI を公開しています。
これからも進化が楽しみなので、ぜひ試してみてください。


脚注
  1. vue.jsもv0のポストによればvue.js単体であればサポートされており、サードパーティのパッケージも時期にサポートされる予定のようです。
    https://x.com/v0/status/1828469187083202673 ↩︎

chot Inc. tech blog

Discussion