前幾天一路從 Gemini 的回傳格式,做到 Structured Output、Schema、Array,昨天又把 JSON 對應成 TypeScript 的 LearningCard 型別。
做到這裡,資料大概已經長這樣:
type LearningCard = {
emoji: string;
title: string;
content: string;
question: string;
answer: string;
};
照以前的開發方式,下一步大概就是開一個 React 專案,開始建 Component、寫樣式,再慢慢把 Learning Card 畫面刻出來。
但都已經做到 Build on Google AI 了,我突然想到另一個問題:
現在的第一版 UI,真的還需要全部自己從零開始刻嗎?
既然 Gemini 可以幫我產生學習內容,那前端 UI 是不是也可以先交給 AI 試試看?
所以今天先不急著自己寫 Component,我想直接把目前已經定義好的 LearningCard 丟給 Gemini,看看它會怎麼設計第一版 UI。
Day 10~Day 12,我一直開著 Structured Output,因為當時的目標很明確:
我要 Gemini 回傳符合指定結構的 Learning Card JSON。
但今天的任務不一樣。
今天不是要 Gemini 回傳:
{
"emoji": "🐢",
"title": "海龜"
}
而是希望它幫我產生 React UI 的程式碼。
因此這次我另外開了一個新的對話,選擇 Gemini 3.8 Flash,並關閉 Structured Output,避免前面設定的 Learning Card Schema 限制這次的回傳內容。
今天我要測的不是「JSON 能不能固定格式」,而是:
只告訴 Gemini 資料格式與基本 UI 需求,它能不能自己完成第一版 React 介面?
我沒有特別指定畫面 Layout,也沒有告訴 Gemini Component 應該怎麼拆。
Prompt 如下:
我正在製作一個智慧學習卡 Web App。
請幫我建立第一版 Learning Card UI,使用 React + TypeScript。
一張 Learning Card 的資料格式如下:
type LearningCard = {
emoji: string;
title: string;
content: string;
question: string;
answer: string;
};
目前的學習對象是 3~4 歲幼兒,
內容主題是海洋動物。
UI 需要顯示:
- Emoji
- 標題
- 學習內容
- 問題
- 答案
請設計成簡單、清楚、適合學習卡使用的介面。
其實要求很單純。
我只指定了:
React + TypeScript
LearningCard 資料格式
3~4 歲幼兒
海洋動物
需要顯示的五個欄位
至於畫面怎麼排、要不要有互動、要用什麼 CSS,我都沒有指定。
Gemini 回傳了一個完整的 LearningCardApp.tsx。
首先,它保留了前一天定義的資料結構:
export type LearningCard = {
emoji: string;
title: string;
content: string;
question: string;
answer: string;
};
接著自己建立了幾張海洋動物的範例資料:
const oceanCards: LearningCard[] = [
{
emoji: "🐳",
title: "藍鯨",
content:
"藍鯨是世界上體型最大的動物,頭頂會噴出好高好高的水花喔!",
question: "誰是海裡最大的動物呢?",
answer: "就是藍鯨!",
},
// ...
];
這部分其實還在預期之內。
真正讓我注意到的是,它沒有只做一張靜態 Card。
Gemini 自己加入了 State:
const [currentIndex, setCurrentIndex] = useState(0);
const [showAnswer, setShowAnswer] = useState(false);
const currentCard = oceanCards[currentIndex];
currentIndex 負責現在顯示第幾張學習卡,而 showAnswer 則負責答案是否顯示。
例如問題區塊,它沒有直接把答案顯示出來,而是先產生一顆按鈕:
{showAnswer ? (
<div
onClick={() => setShowAnswer(false)}
className="w-full bg-emerald-500 text-white font-bold text-center py-2.5 px-4 rounded-xl shadow-md cursor-pointer animate-bounce"
>
🎉 {currentCard.answer}
</div>
) : (
<button
onClick={() => setShowAnswer(true)}
className="w-full bg-amber-400 hover:bg-amber-500 text-amber-950 font-bold py-2.5 px-4 rounded-xl shadow-sm transition active:scale-95"
>
點我看答案 👀
</button>
)}
使用者必須先想一下,再按:
點我看答案 👀
才會看到答案。
但回頭看我的 Prompt,我其實根本沒有要求這個功能。
Gemini 還自己加入:
const handlePrev = () => {
setShowAnswer(false);
setCurrentIndex((prev) =>
prev === 0 ? oceanCards.length - 1 : prev - 1
);
};
const handleNext = () => {
setShowAnswer(false);
setCurrentIndex((prev) =>
prev === oceanCards.length - 1 ? 0 : prev + 1
);
};
搭配底部的:
<button onClick={handlePrev}>
⬅️ 上一張
</button>
<button onClick={handleNext}>
下一張 ➡️
</button>
因此 Gemini 產生的已經不只是:
JSON → 畫面
而比較接近:
LearningCard[]
↓
React State
↓
顯示目前卡片
↓
查看答案
↓
上一張 / 下一張
第一版 Learning Card 的基本互動,其實已經出現了。
另外一個很明顯的地方,是 Gemini 自己選擇使用 Tailwind CSS。
例如:
<div className="min-h-screen bg-sky-50 flex flex-col items-center justify-center p-4">
以及:
<div className="w-full max-w-sm bg-white rounded-3xl shadow-xl border-4 border-sky-200">
從 bg-sky-50、rounded-3xl、shadow-xl 到按鈕的 active:scale-95,整個 UI 都直接使用 Tailwind utility classes。
這點對我來說滿有意思。
因為我的 Prompt 只有:
使用 React + TypeScript。
並沒有寫:
請使用 Tailwind CSS。
也就是說,Tailwind 是 Gemini 自己做的技術選擇。
這也代表如果真的要把這段程式碼放進自己的專案,還是要先確認目前專案的技術環境與 Dependency 是否符合。
AI 可以產生 Code,不代表複製貼上之後一定什麼都不用管。
前端工程師還沒這麼快失業(笑)。
把第一次結果重新看一遍,我發現今天最有趣的其實不是:
Gemini 會寫 React。
這件事現在好像已經沒有那麼令人意外了。
比較值得注意的是,我原本只提出:
顯示 Emoji
顯示標題
顯示內容
顯示問題
顯示答案
Gemini 卻自己補成:
Learning Card
├── 幼兒化的視覺設計
├── 多張學習卡
├── 卡片進度
├── 隱藏答案
├── 查看答案互動
├── 上一張
└── 下一張
換句話說,它不是單純照著規格把五個欄位 Render 出來,而是根據「學習卡」這個使用情境,自己推導了一些它認為合理的互動。
這當然很方便。
但從工程角度來看,也出現了一個新的問題:
AI 覺得合理的功能,就等於產品真的需要嗎?
答案顯然不能直接畫上等號。
如果只是做 Prototype,這個結果其實已經讓我省下不少從零開始刻 UI 的時間。
但如果真的要繼續開發,我還是會開始檢查很多事情。
例如目前 LearningCard Type、範例資料和 UI 都放在同一份程式碼裡,之後可能會再拆成不同檔案;Gemini 自己產生的學習內容也還只是 Mock Data,不能直接當成最後教材。
更重要的是,目前這份:
const oceanCards: LearningCard[] = [...]
並不是 Day 12 Gemini Structured Output 回傳的那五張 Learning Card。
所以現在其實還是:
AI 產生範例資料
+
AI 產生 UI
↓
第一版 Prototype
而不是我們真正想完成的:
使用者輸入學習需求
↓
Gemini
↓
Structured Output
↓
LearningCardResponse
↓
LearningCard[]
↓
React UI
這兩件事情差很多。
原本我以為 Day 14 會是「終於開始寫 React」。
結果真正做完之後,反而幾乎沒有自己從零開始刻 UI。
我只提供了資料格式、使用對象以及最基本的畫面需求,Gemini 就幫我產生了一版 React + TypeScript UI,甚至自己加入 Tailwind CSS、State、查看答案與卡片切換。
這讓第一版 Prototype 的起點快了很多。
但同時也讓我更清楚看到:
AI 可以幫我快速產生第一版,但工程師還是要決定哪些東西該留下來。
以前可能是先想好 UI,再一行一行把 Component 寫出來。
現在則多了一種工作方式:
定義需求
↓
讓 AI 產生第一版
↓
實際執行
↓
Code Review
↓
保留 / 修改 / 刪除
↓
再整合真正的資料
所以「現在還需要自己刻前端嗎?」
至少以今天的實驗來看,第一版不一定要全部從零開始刻,但也還不到可以完全不管的程度。
而且今天做到這裡時,我本來以為實驗已經結束了。
結果回到 Gemini 的回覆選單,我又看到了一個選項:
Make this an app
看來事情還沒結束(笑)。
今天 Gemini 幫我產生了第一版 LearningCardApp.tsx。
如果按照一般開發流程,接下來應該就是建立專案、安裝套件,再把這些程式碼真正跑起來。
但既然 Gemini 已經提供了 Make this an app,明天就來試試看:
如果連「把程式碼變成 App」這一步也交給 AI,最後會發生什麼事?
而這次 Gemini 做的,似乎比我原本要求的 Learning Card 還要多很多。