2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Gemini APIとReactを使ってチャットアプリを作る

Last updated at Posted at 2025-07-20

こんにちは
株式会社クラスアクト インフラストラクチャ事業部の大塚です。

Viteを使ったReact環境にGemini APIを連携してチャットアプリを作っていきたいと思います。
※当方インフラエンジニアです。フロントエンド関係は勉強中です。なので詳しいことは書けません。自分用のメモ的なサムシングです。
※細かいことはこれから勉強です。

GitHub

最終的なフォルダ構成

test@DAIV-SHOLAND:/mnt/c/Users/ohtsu/Documents/react_dev/react-chat-app$ tree -L 2
.
├── README.md
├── config
│   ├── config-sample.js
│   └── config.js
├── eslint.config.js
├── index.html
├── node_modules
│   ├── @ampproject
│   ├── @babel
│   ├── @esbuild
│   ├── @eslint
│   ├── @eslint-community
│   ├── @google
│   ├── @humanfs
│   ├── @humanwhocodes
│   ├── @jridgewell
│   ├── @lit
│   ├── @lit-labs
│   ├── @material
│   ├── @rolldown
│   ├── @rollup
│   ├── @types
│   ├── @vitejs
│   ├── acorn
│   ├── acorn-jsx
│   ├── ajv
│   ├── ansi-styles
│   ├── argparse
│   ├── balanced-match
│   ├── brace-expansion
│   ├── browserslist
│   ├── callsites
│   ├── caniuse-lite
│   ├── chalk
│   ├── color-convert
│   ├── color-name
│   ├── concat-map
│   ├── convert-source-map
│   ├── cross-spawn
│   ├── csstype
│   ├── debug
│   ├── deep-is
│   ├── electron-to-chromium
│   ├── esbuild
│   ├── escalade
│   ├── escape-string-regexp
│   ├── eslint
│   ├── eslint-plugin-react-hooks
│   ├── eslint-plugin-react-refresh
│   ├── eslint-scope
│   ├── eslint-visitor-keys
│   ├── espree
│   ├── esquery
│   ├── esrecurse
│   ├── estraverse
│   ├── esutils
│   ├── fast-deep-equal
│   ├── fast-json-stable-stringify
│   ├── fast-levenshtein
│   ├── fdir
│   ├── file-entry-cache
│   ├── find-up
│   ├── flat-cache
│   ├── flatted
│   ├── gensync
│   ├── glob-parent
│   ├── globals
│   ├── has-flag
│   ├── ignore
│   ├── import-fresh
│   ├── imurmurhash
│   ├── is-extglob
│   ├── is-glob
│   ├── isexe
│   ├── js-tokens
│   ├── js-yaml
│   ├── jsesc
│   ├── json-buffer
│   ├── json-schema-traverse
│   ├── json-stable-stringify-without-jsonify
│   ├── json5
│   ├── keyv
│   ├── levn
│   ├── lit
│   ├── lit-element
│   ├── lit-html
│   ├── locate-path
│   ├── lodash.merge
│   ├── lru-cache
│   ├── minimatch
│   ├── ms
│   ├── nanoid
│   ├── natural-compare
│   ├── node-releases
│   ├── optionator
│   ├── p-limit
│   ├── p-locate
│   ├── parent-module
│   ├── path-exists
│   ├── path-key
│   ├── picocolors
│   ├── picomatch
│   ├── postcss
│   ├── prelude-ls
│   ├── punycode
│   ├── react
│   ├── react-dom
│   ├── react-refresh
│   ├── resolve-from
│   ├── rollup
│   ├── scheduler
│   ├── semver
│   ├── shebang-command
│   ├── shebang-regex
│   ├── source-map-js
│   ├── strip-json-comments
│   ├── supports-color
│   ├── tinyglobby
│   ├── tslib
│   ├── type-check
│   ├── update-browserslist-db
│   ├── uri-js
│   ├── vite
│   ├── which
│   ├── word-wrap
│   ├── yallist
│   └── yocto-queue
├── package-lock.json
├── package.json
├── public
│   └── vite.svg
├── src
│   ├── App.css
│   ├── App.jsx
│   ├── assets
│   ├── gemini.js
│   ├── index.css
│   ├── main.jsx
│   └── org
└── vite.config.js

React+Vite環境の用意

npm create vite@latest .
npm install

Gemini APIの発行

これにアクセス。

image (36).png

右上にGet API Keyという表示があるのでこれを押下する。
image (37).png

APIキーを作成ボタンがあるので、これを押下する。
"gemini-2.0-flash:generateContent"と記載されており、これは後程使うのでメモっておく。
image (38).png

新しいプロジェクトでAPIキーを作成を押下する。
image (39).png

作成出来た。
image (40).png

React関連のコード修正

必要なものの追加インストール

C:\Users\ohtsu\Documents\react_dev\react-chat-app>npm install @material/web

added 53 packages, and audited 206 packages in 14s

33 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

C:\Users\ohtsu\Documents\react_dev\react-chat-app>npm install @google/generative-ai

added 1 package, and audited 207 packages in 2s

33 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

package.jsonを念のため確認する。

{
  "name": "chat",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "@google/generative-ai": "^0.24.1",
    "@material/web": "^2.3.0",
    "react": "^19.1.0",
    "react-dom": "^19.1.0"
  },
  "devDependencies": {
    "@eslint/js": "^9.30.1",
    "@types/react": "^19.1.8",
    "@types/react-dom": "^19.1.6",
    "@vitejs/plugin-react": "^4.6.0",
    "eslint": "^9.30.1",
    "eslint-plugin-react-hooks": "^5.2.0",
    "eslint-plugin-react-refresh": "^0.4.20",
    "globals": "^16.3.0",
    "vite": "^7.0.4"
  }
}

コード修正

App.jsx

import './App.css';
import React, { useRef, useState } from 'react';
import '@material/web/button/filled-button';
import '@material/web/textfield/filled-text-field';
import { geminiRun } from './gemini'; // gemini.jsからインポート

function App() {
  const inputRef = useRef(null);
  const [messages, setMessages] = useState([]); // メッセージのステートを追加

  const onButtonClick = async () => {
    if (!inputRef.current) { return; }

    const inputText = inputRef.current.value;
    const response = await geminiRun(inputText);
    
    // 新しいメッセージを追加
    setMessages((prevMessages) => [
      ...prevMessages,
      { text: inputText, type: 'user' }, // ユーザーのメッセージ
      { text: response, type: 'gemini' }  // Geminiの応答
    ]);

    // 入力フィールドをクリア
    inputRef.current.value = '';
  }

  return (
    <div className="chat-container">
      <div className="chat-window">
        <div className="messages">
          {messages.map((msg, index) => (
            <div key={index} className={`message ${msg.type}`}>
              {msg.text}
            </div>
          ))}
        </div>
        <div className="input-container">
          <md-filled-text-field
            id="input"
            type="textarea"
            rows="5"
            ref={inputRef}
            className="message-input"
          />
          <md-filled-button 
            type="button" 
            id="button" 
            className="send-button" 
            onClick={onButtonClick}
          >
            Send
          </md-filled-button>
        </div>
      </div>
    </div>
  );
}

export default App;

App.css

/* App.css */
body {
  margin: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: 100vh;
  background-color: #f0f0f0;
}

.chat-container {
  width: 100%;
  max-width: 800px;
  min-width: 800px; /* 文字が入力されていなくても800pxの横幅を持たせる */
  border: 1px solid #ccc;
  border-radius: 8px;
  overflow: hidden;
  background-color: white;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  margin-bottom: 30px;
}

.chat-window {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.messages {
  flex: 1;
  padding: 10px;
  overflow-y: auto; /* スクロール機能の追加 */
  border-bottom: 1px solid #ccc;
  max-height: 400px; /* 高さを固定 */
}

.message {
  padding: 8px;
  margin: 5px 0;
  border-radius: 5px;
  background-color: #e1ffc7;
}

.message.user {
  background-color: #e1ffc7; /* ユーザーのメッセージの背景色 */
}

.message.gemini {
  background-color: #d1e7ff; /* Geminiのメッセージの背景色 */
}

.input-container {
  display: flex;
  padding: 10px;
  background-color: #f9f9f9;
}

.message-input {
  flex: 1;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  margin-right: 10px;
  resize: none; /* サイズ変更を無効にする */
  overflow: auto; /* オーバーフロー時にスクロールバーを表示 */
}

.send-button {
  padding: 10px 15px;
  border: none;
  border-radius: 5px;
  background-color: #007bff;
  color: white;
  cursor: pointer;
}

.send-button:hover {
  background-color: #0056b3;
}

@media (max-width: 800px) {
  .chat-container {
    max-width: 100%; /* 小さい画面では100%の幅にする */
    min-width: 400px; /* 文字が入力されていなくても400pxの横幅を持たせる */
  }
}

gemini.js

srcディレクトリ配下にgemini.jsを作成して中身を以下とする

// src/gemini.js
import { GoogleGenerativeAI } from "@google/generative-ai";
import { API_KEY } from "../config/config.js";

const genAI = new GoogleGenerativeAI(API_KEY);

export async function geminiRun(prompt) {
  const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash" }); // モデル名はAPIキー作成した時に確認したもの

  const result = await model.generateContent(prompt);
  const response = await result.response;
  const text = await response.text(); // ここでawaitを使用してテキストを取得
  console.log(text);

  return text;
}

config.js

Gemini APIのデータをコンフィグファイルに記載する

../config/config.js
export const API_KEY = 'YOUR_API_KEY_HERE'; // ここに実際のAPIキーを入力

.gitignore

GitHubでコードを管理する場合、.gitignoreを以下のように記述する

config/config.js

アプリ起動

C:\Users\ohtsu\Documents\react_dev\react-chat-app>npm run dev

> chat@0.0.0 dev
> vite


  VITE v7.0.5  ready in 157 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

Gemini AIさんとAPIを使って会話が出来ました。
緑背景が人間、青背景がGeminiさんです。
image (41).png

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?