Vercelの v0とCloudflare Workers

Page content

Vercelの v0 でUIを作り、それを Cloudflare Workers のAIバックエンドに接続する手順は、驚くほどスムーズです。

フロントエンド(見た目)は v0、バックエンド(知能)は Cloudflare という最強の組み合わせを実現する具体的なステップを解説します。


1. v0 で「オシャレなチャットUI」を生成する

まず、v0.dev にアクセスし、プロンプトを入力します。

入力プロンプト例:

「モダンで清潔感のある、営業支援AIチャットのUIを作って。右下にフローティングボタンがあり、クリックするとチャット窓が開く形式。Next.js, Tailwind CSS, Lucide Reactを使用。メッセージ入力欄と、AIの回答エリア、クイック返信ボタンを付けて。」

生成されたコードの中で、「送信ボタン」を押した時の処理handleSubmit など)を、後でCloudflareに接続するために確認しておきます。


2. Cloudflare Workers AI でバックエンドを準備する

Cloudflare側で、AIが回答を返すための「エンドポイント」を作ります。

** wranglerでのセットアップ例:**

Bash

npm create cloudflare@latest chat-backend -- --template worker

index.ts(バックエンドのコード例):

CloudflareのWorkers AI(Llama 3など)を使って、送られてきたメッセージに返答するAPIを作成します。

TypeScript

export default {
  async fetch(request, env) {
    const { message } = await request.json();

    // AIモデル(Llama-3など)を呼び出す
    const response = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
      messages: [
        { role: 'system', content: 'あなたは優秀な営業支援アシスタントです。' },
        { role: 'user', content: message }
      ]
    });

    return new Response(JSON.stringify(response), {
      headers: { 
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*' // フロントエンドからのアクセスを許可
      }
    });
  }
}

これを npx wrangler deploy で公開し、発行された URL をメモします。


3. v0 のコードを Cloudflare に接続する

v0で生成した React コンポーネント内の sendMessage 関数などを、CloudflareのURLを叩くように書き換えます。

フロントエンド(React)の修正イメージ:

JavaScript

const handleSendMessage = async (text) => {
  // 1. ユーザーのメッセージを画面に表示
  setMessages([...messages, { role: 'user', content: text }]);

  // 2. Cloudflare Worker の API を叩く
  const response = await fetch('https://your-worker-url.workers.dev', {
    method: 'POST',
    body: JSON.stringify({ message: text }),
  });

  const data = await response.json();

  // 3. AIの回答を画面に表示
  setMessages(prev => [...prev, { role: 'assistant', content: data.response }]);
};

4. 組み込みと公開

最後に、このUIを自分のサイトに載せる方法です。

  • Next.jsを使っている場合: v0のコードをそのままプロジェクトの components フォルダに入れ、CloudflareのURLを環境変数として設定します。

  • 既存のHTMLサイト(WordPressなど)の場合:

    v0で作ったコンポーネントを Cloudflare Pages にデプロイし、<iframe> や外部スクリプトとして既存サイトに埋め込みます。


この構成のメリット

  1. デザインがプロ級: v0を使うことで、エンジニアが苦手な「イケてるUI」が数秒で手に入ります。

  2. 運用コストがほぼゼロ: Cloudflare Workers AI の無料枠は非常に広いため、初期段階では月額費用をかけずに運用できます。

  3. スピード: Vercel (UI) も Cloudflare (AI) もエッジネットワークで動くため、ユーザーへのレスポンスが非常に高速です。

v0で生成されたコードは通常、見た目(UI)に特化しているため、ロジック部分は「ダミーのデータ」や「空の関数」になっていることが多いです。

これを Cloudflare Workers AI に接続するために書き換えるべきポイントは、主に 「状態管理(Messages)」「送信関数(handleSubmit)」 の2箇所です。


1. メッセージの状態管理を準備する

v0のコード内で、チャット履歴を保持している useState を探します。

TypeScript

// v0が生成した初期コードのイメージ
const [messages, setMessages] = useState([
  { id: 1, role: "assistant", content: "こんにちは!営業支援AIです。何かお手伝いしましょうか?" }
]);
const [input, setInput] = useState("");
const [isLoading, setIsLoading] = useState(false); // ローディング状態を追加

2. 送信関数(Submit)の書き換え

ここが一番重要な「接続」部分です。fetch を使って Cloudflare Worker の URL を叩くように変更します。

TypeScript

const handleSubmit = async (e: React.FormEvent) => {
  e.preventDefault();
  if (!input.trim() || isLoading) return;

  const userMessage = { id: Date.now(), role: "user", content: input };
  
  // 1. ユーザーの入力を画面に即座に反映
  setMessages((prev) => [...prev, userMessage]);
  setInput("");
  setIsLoading(true);

  try {
    // 2. Cloudflare Worker のエンドポイントにリクエストを送る
    const response = await fetch("https://your-worker-name.workers.dev/", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ message: input }), // Worker側で受け取るキー名に合わせる
    });

    if (!response.ok) throw new Error("Network response was not ok");

    const data = await response.json();

    // 3. AIからの返答を画面に追加
    setMessages((prev) => [
      ...prev,
      { id: Date.now() + 1, role: "assistant", content: data.response } 
    ]);
  } catch (error) {
    console.error("Error:", error);
    // エラー時の処理(ユーザーへの通知など)
  } finally {
    setIsLoading(false);
  }
};

3. ローディング表示(UXの向上)

AIが考えている間、ユーザーに「入力中…」と見せるための工夫です。v0のJSX(HTML部分)で、送信ボタンやメッセージエリアを以下のように調整します。

TypeScript

{/* メッセージ表示エリアの末尾付近 */}
{isLoading && (
  <div className="flex justify-start mb-4">
    <div className="bg-gray-100 p-3 rounded-lg animate-pulse">
      AIが回答を生成中...
    </div>
  </div>
)}

{/* 送信ボタン */}
<button type="submit" disabled={isLoading}>
  {isLoading ? <LoaderIcon className="animate-spin" /> : "送信"}
</button>

4. 接続時の「落とし穴」:CORS設定

ブラウザから Cloudflare Worker を叩く際、CORS(読み取り制限) でエラーが出ることがあります。Cloudflare Worker 側のコードに、以下のヘッダーを追加するのを忘れないでください。

Cloudflare Worker 側の修正例:

TypeScript

return new Response(JSON.stringify({ response: aiResult }), {
  headers: {
    "Content-Type": "application/json",
    "Access-Control-Allow-Origin": "*", // 開発中は全て許可、本番は自サイトのURLに制限
    "Access-Control-Allow-Methods": "POST, OPTIONS",
    "Access-Control-Allow-Headers": "Content-Type",
  },
});

💡 営業支援を強化する小技:システムプロンプトの工夫

Cloudflare Worker 側で AI に送る system メッセージに以下のような指示を混ぜるだけで、営業としての精度が激変します。

“あなたはB2B営業のプロです。回答は簡潔にし、ユーザーが困りごとを話したら、自社製品の『〇〇機能』がどう解決できるかを具体的に提示してください。最後に必ずアポイント調整用のリンクを提示してください。”

これで、v0のオシャレな見た目と、Cloudflareの賢い脳が繋がった「営業マシン」が完成します!

v0で作成した「オシャレなUI」を自分の既存サイト(WordPress、静的HTML、Next.jsなど)に埋め込むには、主に3つのパターンがあります。

サイトの構造に合わせて最適なものを選んでください。


パターン1:既存のHTMLサイト(WordPressなど)に「チャット窓」として埋め込む

最も汎用的な方法です。v0で作ったReactアプリをビルドして、1行の<script>タグで呼び出せるようにします。

手順:

  1. Cloudflare Pages などにv0のプロジェクトをデプロイし、公開URL(例: https://my-ai-chat.pages.dev)を取得します。

  2. 自分のサイトの </body> タグの直前に以下のコードを貼り付けます。

HTML

<div id="ai-chat-root"></div>

<script type="module">
  import { mountChat } from 'https://my-ai-chat.pages.dev/widget.js';
  mountChat(document.getElementById('ai-chat-root'), {
    apiKey: 'your-api-key', // 必要に応じて
    initialMessage: 'こんにちは!営業支援AIです。何かお困りですか?'
  });
</script>

<link rel="stylesheet" href="https://my-ai-chat.pages.dev/style.css">

パターン2:iframe(アイフレーム)で安全に埋め込む

サイトのCSS干渉を避けたい場合や、手軽に試したい場合に最適です。

HTML

<div style="position: fixed; bottom: 20px; right: 20px; z-index: 9999;">
  <iframe 
    src="https://my-ai-chat.pages.dev" 
    width="400" 
    height="600" 
    style="border: none; border-radius: 15px; box-shadow: 0 4px 12px rgba(0,0,0,0.15);"
    allow="clipboard-write"
  ></iframe>
</div>
  • メリット: 親サイトのデザイン(CSS)が崩れない。

  • デメリット: モバイルでの挙動調整が少し難しい。


パターン3:Next.js / React サイトに「コンポーネント」として直接入れる

v0はNext.jsベースのコードを生成するため、すでにReact系のサイトを運営しているなら、これが最も高速でスムーズです。

components/SalesAIChat.tsx(v0からコピーしたコード):

TypeScript

import { useState } from 'react'

export default function SalesAIChat() {
  const [isOpen, setIsOpen] = useState(false)
  // ... v0のロジック ...

  return (
    <div className="fixed bottom-4 right-4 z-50">
      {isOpen ? (
        <div className="w-80 h-[500px] bg-white rounded-lg shadow-xl flex flex-col">
          {/* チャット画面のJSX */}
          <button onClick={() => setIsOpen(false)}>閉じる</button>
        </div>
      ) : (
        <button 
          onClick={() => setIsOpen(true)}
          className="bg-blue-600 p-4 rounded-full text-white shadow-lg"
        >
          AIに相談
        </button>
      )}
    </div>
  )
}

⚠️ 埋め込み時の最重要ポイント:CORS設定

自分のサイト(https://example.com)から Cloudflare Worker(https://my-worker.workers.dev)にリクエストを送る際、ブラウザがセキュリティ制限をかけます。

Cloudflare Worker 側で必ず以下を設定してください:

TypeScript

// Workerのレスポンスヘッダー例
const responseHeaders = {
  "Content-Type": "application/json",
  "Access-Control-Allow-Origin": "https://your-site.com", // 自分のサイトURLに制限する
  "Access-Control-Allow-Methods": "POST, OPTIONS",
  "Access-Control-Allow-Headers": "Content-Type",
};

// OPTIONSリクエスト(プリフライト)への対応
if (request.method === "OPTIONS") {
  return new Response(null, { headers: responseHeaders });
}

まとめ:どれを選べばいい?

  • WordPressや静的HTMLを使っている:パターン1(Script) または パターン2(iframe)

  • 最新のWebアプリを自作している:パターン3(Component)

まずは パターン2(iframe) で、自分のサイトにチャット窓が表示されるか試してみるのが最も簡単でトラブルが少ないです。

次に、「AIが会社の資料を元に答えるようにする(Vectorize)」 の設定方法に進みますか? それとも 「営業メールを自動送信する機能(ツール呼び出し)」 の作り方を見ますか?