Step 2 Gemini Chat Api

📘 Tutorial: Build a Gemini Chat App Using Your Own API Key


🔍 Overview — What We Will Build & How

✅ What we will build

A single-page Gemini Chat web app that:

  • Lets users generate and use their own Gemini API key

  • Stores the API key safely in the browser

  • Supports model selection (flash / flash-lite / pro)

  • Shows a “Thinking…” message while Gemini responds

  • Renders Markdown responses (code blocks, lists, etc.)

  • Allows users to copy Gemini responses with one click

🧩 How we will build it

  • One file: index.html

  • Technologies used:

    • HTML (structure)
    • Bootstrap (UI)
    • JavaScript + fetch (Gemini API)
    • localStorage (save API key)
    • marked.js (Markdown rendering)

We’ll do this in 3 easy steps.


🧩 STEP 1 — UI + API Key Guidance & Storage

What happens in this step

  • Show a guided API key setup screen

  • Explain how to generate a Gemini API key

  • Save the key in localStorage

  • Automatically skip setup if key already exists

  • Prepare the full chat UI (hidden initially)

How to know you’re on the right track

✅ You see the API key card

✅ Clicking Continue opens the chat

✅ Refreshing the page skips setup


✅ Step 1 Code

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Gemini Chat</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">

  <style>
    body { background:#f5f7fb; }
    .center { min-height:100vh; display:flex; align-items:center; justify-content:center; }
    .chat { height:90vh; }
    .messages { flex:1; overflow:auto; background:#fff; padding:1rem; border-radius:.75rem; }
    .msg { max-width:75%; padding:.5rem .8rem; border-radius:1rem; margin-bottom:.5rem; }
    .user { margin-left:auto; background:#e7f0ff; }
    .bot { background:#f1f1f1; }
    textarea { resize:none; max-height:7.5rem; }
  </style>
</head>
<body>

<!-- API KEY SETUP -->
<div id="setup" class="center">
  <div class="card shadow-sm" style="max-width:420px;width:100%">
    <div class="card-body text-center">
      <h5 class="mb-3">Get free Gemini API Key</h5>
      <p class="small text-muted mb-3">
        1. Open <a href="https://aistudio.google.com/app/apikey" target="_blank">Google AI Studio</a><br>
        2. Click <strong>Create API key</strong><br>
        3. Copy & paste it below
      </p>
      <input id="apiKey" class="form-control mb-3 text-center" placeholder="Paste API Key">
      <button class="btn btn-primary w-100" onclick="start()">Continue</button>
    </div>
  </div>
</div>

<!-- CHAT UI -->
<div id="chat" class="container d-none chat">
  <div class="d-flex flex-column h-100 py-3">
    <h5 class="text-center mb-2">💬 Gemini Chat</h5>
    <div id="messages" class="messages mb-2"></div>
    <textarea id="prompt" class="form-control mb-2" placeholder="Type message..."></textarea>
    <button class="btn btn-primary" onclick="send()">Send</button>
  </div>
</div>

<script>
  const messages = document.getElementById("messages");

  // Auto open chat if API key already saved
  if (localStorage.GEMINI_KEY) showChat();

  function start() {
    const key = apiKey.value.trim();
    if (!key) return alert("API key required");
    localStorage.GEMINI_KEY = key;
    showChat();
  }

  function showChat() {
    setup.classList.add("d-none");
    chat.classList.remove("d-none");
  }

  function send() {
    alert("Next step: call Gemini API");
  }
</script>

</body>
</html>

🧩 STEP 2 — Call Gemini API + Show “Thinking…” + Model Selection

What happens in this step

  • User message appears instantly

  • A “Thinking…” message is shown

  • Selected Gemini model is used

  • Gemini response replaces “Thinking…”

How to know you’re on the right track

✅ “Thinking…” appears while waiting

✅ Response comes from selected model


✅ Step 2 Code (replace only <script>)

<script>
  const messages = document.getElementById("messages");

  if (localStorage.GEMINI_KEY) showChat();

  function start() {
    const key = apiKey.value.trim();
    if (!key) return alert("API key required");
    localStorage.GEMINI_KEY = key;
    showChat();
  }

  function showChat() {
    setup.classList.add("d-none");
    chat.classList.remove("d-none");
  }

  function add(text, cls) {
    const d = document.createElement("div");
    d.className = `msg ${cls}`;
    d.textContent = text;
    messages.appendChild(d);
    messages.scrollTop = messages.scrollHeight;
    return d;
  }

  async function send() {
    const text = prompt.value.trim();
    if (!text) return;
    prompt.value = "";

    add(text, "user");
    const thinking = add("Thinking...", "bot");

    const model = document.querySelector('input[name="model"]:checked')?.value || "gemini-2.5-flash";

    try {
      const res = await fetch(
        `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${localStorage.GEMINI_KEY}`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            contents: [{ parts: [{ text }] }]
          })
        }
      );

      const data = await res.json();
      thinking.remove();
      add(data?.candidates?.[0]?.content?.parts?.[0]?.text || "No response", "bot");

    } catch {
      thinking.remove();
      add("Error occurred", "bot");
    }
  }
</script>

🧩 STEP 3 — Markdown Rendering + Copy Response (Final)

What happens in this step

  • Gemini responses render as Markdown

  • Code blocks, lists, links are styled

  • Each bot message has a Copy button

How to know you’re on the right track

✅ Code blocks look formatted

✅ Clicking Copy copies the response


✅ FINAL STEP — Full Final Code

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Gemini Chat</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">

  <!-- marked (markdown renderer) -->
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>

  <style>
    body { background:#f5f7fb; }
    .center { min-height:100vh; display:flex; align-items:center; justify-content:center; }
    .chat { height:90vh; }
    .messages { flex:1; overflow:auto; background:#fff; padding:1rem; border-radius:.75rem; }
    .msg { max-width:75%; padding:.5rem .8rem; border-radius:1rem; margin-bottom:.5rem; }
    .user { margin-left:auto; background:#e7f0ff; }
    .bot { background:#f1f1f1; }
    .thinking { font-style:italic; opacity:.75; }
    textarea { resize:none; max-height:7.5rem; }

    /* markdown friendly */
    .msg pre { margin:.5rem 0 0; padding:.75rem; border-radius:.5rem; background:#111; color:#eee; overflow:auto; }
    .msg code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
    .msg p { margin:0 0 .5rem; }
    .msg p:last-child { margin-bottom:0; }
    .msg ul, .msg ol { margin:.4rem 0 .4rem 1.25rem; }
    .msg blockquote { margin:.5rem 0; padding-left:.75rem; border-left:3px solid #ccc; color:#555; }
    .msg a { word-break: break-word; }
  </style>
</head>
<body>

<!-- API KEY -->
<div id="setup" class="center">
  <div class="card shadow-sm" style="max-width:420px;width:100%">
    <div class="card-body text-center">
      <h5 class="mb-3">Get free Gemini API Key</h5>
      <p class="small text-muted mb-3">
        1. Open <a href="https://aistudio.google.com/app/apikey" target="_blank">Google AI Studio</a><br>
        2. Click <strong>Create API key</strong><br>
        3. Copy & paste it below
      </p>
      <input id="apiKey" class="form-control mb-3 text-center" placeholder="Paste API Key">
      <button class="btn btn-primary w-100" onclick="start()">Continue</button>
    </div>
  </div>
</div>

<!-- CHAT -->
<div id="chat" class="container d-none chat">
  <div class="d-flex flex-column h-100 py-3">

    <h5 class="text-center mb-2">💬 Gemini Chat</h5>

    <div id="messages" class="messages shadow-sm mb-2"></div>

    <textarea id="prompt" rows="1" class="form-control mb-2"
      placeholder="Type message..."
      oninput="this.style.height='auto';this.style.height=Math.min(this.scrollHeight,120)+'px'"></textarea>

    <div class="d-flex justify-content-between align-items-center">
      <div class="d-flex gap-3">
        <div class="form-check">
          <input class="form-check-input" type="radio" id="m1" name="model" value="gemini-2.5-flash" checked>
          <label class="form-check-label" for="m1">gemini-2.5-flash</label>
        </div>
        <div class="form-check">
          <input class="form-check-input" type="radio" id="m2" name="model" value="gemini-2.5-flash-lite">
          <label class="form-check-label" for="m2">gemini-2.5-flash-lite</label>
        </div>
        <div class="form-check">
          <input class="form-check-input" type="radio" id="m3" name="model" value="gemini-2.5-pro">
          <label class="form-check-label" for="m3">gemini-2.5-pro</label>
        </div>
      </div>

      <button class="btn btn-primary" onclick="send()">Send</button>
    </div>

  </div>
</div>

<script>
  const messages = document.getElementById("messages");

  if (localStorage.GEMINI_KEY) showChat();

  function start() {
    const key = document.getElementById("apiKey").value.trim();
    if (!key) return alert("API key required");
    localStorage.GEMINI_KEY = key;
    showChat();
  }

  function showChat() {
    document.getElementById("setup").classList.add("d-none");
    document.getElementById("chat").classList.remove("d-none");
  }

  function add(text, cls, asMarkdown = false) {
    const wrap = document.createElement("div");
    wrap.className = `msg ${cls}`;

    const content = document.createElement("div");
    if (asMarkdown) content.innerHTML = marked.parse(text);
    else content.textContent = text;

    wrap.appendChild(content);

    if (cls.includes("bot")) {
      const btn = document.createElement("button");
      btn.className = "btn btn-sm btn-outline-secondary mt-2";
      btn.textContent = "Copy";
      btn.onclick = async () => {
        try {
          await navigator.clipboard.writeText(text);
          btn.textContent = "Copied!";
          setTimeout(() => (btn.textContent = "Copy"), 800);
        } catch {
          btn.textContent = "Copy failed";
          setTimeout(() => (btn.textContent = "Copy"), 800);
        }
      };
      wrap.appendChild(btn);
    }

    messages.appendChild(wrap);
    messages.scrollTop = messages.scrollHeight;
    return wrap;
  }

  async function send() {
    const input = document.getElementById("prompt");
    const text = input.value.trim();
    if (!text) return;
    input.value = "";

    add(text, "user");

    const thinkingEl = add("Thinking...", "bot thinking");

    const model = document.querySelector('input[name="model"]:checked').value;

    try {
      const r = await fetch(
        `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${localStorage.GEMINI_KEY}`,
        {
          method:"POST",
          headers:{ "Content-Type":"application/json" },
          body:JSON.stringify({ contents:[{ parts:[{ text }] }] })
        }
      );

      const j = await r.json();
      thinkingEl.remove();

      const reply = j?.candidates?.[0]?.content?.parts?.[0]?.text || "No response";
      add(reply, "bot", true);

    } catch (e) {
      thinkingEl.remove();
      add("Error occurred", "bot");
    }
  }
</script>

</body>
</html>

🔜 Suggested Next To-Dos (Enhancements)

Multiple Google accounts & API key management

  • Create a small PHP script that stores multiple Gemini API keys in a JSON file (one key per Google account or project).

  • Load this JSON on the frontend and show a dropdown to select an account/key before chatting.

  • Use cases:

    • Separate keys for personal / work / demo projects
    • Switch keys when one hits rate limits
    • Team usage where each member uses their own key
  • Example flow:

    • keys.json{ "account_name": "API_KEY" }
    • PHP endpoint → returns available accounts
    • UI dropdown → selects which key to use for the request

Basic backend hardening

  • Move API keys out of the browser into PHP (proxy requests to Gemini).

  • Log usage per account (date, model, token count).

  • Add simple limits per key (daily / hourly).

  • This prepares the app for real production use.


💡 Practical Use Cases With This Simple Code

Portfolio / Website Builder

  • Generate hero text, about sections, services descriptions

  • Create SEO-friendly content per section

  • Rewrite text in different tones (professional, friendly, minimal)

Reports & Documentation

  • Generate project reports, summaries, and explanations

  • Convert raw notes into structured Markdown reports

  • Create release notes or changelogs

Learning & Knowledge Assistant

  • Explain topics step by step (Java, SQL, Spring Boot, etc.)

  • Generate examples and simple explanations

  • Act as a personal study helper

App & Admin Panel Integration

  • Show AI-powered suggestions in forms

  • Help users write descriptions, comments, or messages

  • Provide inline explanations or help tips

Support & Help Systems

  • Answer FAQs using prompt-based guidance

  • Generate troubleshooting steps

  • Explain errors or logs in simple language

0%