๐๏ธ Chapter 12: Supabase Dynamic Database Integration & Rendering
In the previous chapter, we created a stunning Bento Grid with ultimate visual effects. But currently, the text on the cards is "hardcoded" in HTML. If your boss wants to add a new campground, you can't modify the code and redeploy every time, right?
This is why we need a Database. In this lesson, we'll use the most powerful modern Backend-as-a-Service: Supabase. We won't teach you SQL syntax but instead show you: "How to order from AI to fetch data from Supabase and seamlessly integrate it into your React beautiful cards!"
๐ก Practice 1: Establishing Supabase Client Connection
Before requesting data from the database, we need to get a "key" to tell Supabase who we are.
๏ฟฝ๏ฟฝ๏ฟฝ Vibe Prompt Practice 1: Initializing Supabase Connection
[!IMPORTANT] Copy and send this Prompt to AI:
I'm developing a camping website with React and Supabase.Please help me write a supabaseClient.js file to initialize the Supabase connection.My URL and KEY will be stored in environment variables (VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY in .env file).Provide complete code and explain in comments why we store keys in environment variables instead of hardcoding them.
๐ค AI-Generated Real Code:
// supabaseClient.js
import { createClient } from '@supabase/supabase-js';
// Read Supabase project URL and public API key from environment variables
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
// Create and export Supabase client instance
export const supabase = createClient(supabaseUrl, supabaseAnonKey);
/*
ใAI Security Reminderใ:
Why not hardcode keys here? (e.g., directly write "https://xxx.supabase.co")
Because if you push code to GitHub (especially public repositories),
hackers worldwide can see your database keys and delete/modify all your data!
Storing them in .env and excluding .env in .gitignore is the first line of security defense!
*/
๐ฃ Practice 2: Fetching Data from Database
We created a table named campgrounds in Supabase.
It contains name, location, and price.
Now we need to fetch it!
๐ก Vibe Prompt Practice 2: Writing Async Fetch Function
[!IMPORTANT] Copy and send this Prompt to AI:
Using the supabaseClient I just created,help me write an async function getCampgrounds() to fetch all data from the "campgrounds" table in Supabase.If successful, return the data array.If errors occur, log them in console and return an empty array [] to prevent UI crashes.Add detailed Chinese comments.
๐ค AI's Defensive Code:
import { supabase } from './supabaseClient';
export const getCampgrounds = async () => {
try {
// Use supabase.from('table_name').select('*') to fetch all columns
// This takes time, so we need await!
const { data, error } = await supabase
.from('campgrounds')
.select('*');
// Supabase API design: errors won't crash but are stored in error variable
if (error) {
// If error exists, throw it to catch block
throw error;
}
// If successful, return data array
return data;
} catch (error) {
// Safety net: catch error, log it, and return empty array
console.error('Failed to fetch campgrounds โ:', error.message);
return [];
}
};
โณ Practice 3: Frontend State Management & Loading
A classic interview question: "Network requests take time. What happens to the UI during those 1-2 seconds?"
If unhandled, users see blank screens and might close the page.
We'll use React's useState and useEffect to manage loading states!
๐ก Vibe Prompt Practice 3: Complete React Data Flow
[!IMPORTANT] Copy and send this Prompt to AI:
I want to use getCampgrounds in a React component <CampList>.Set up three states (useState):1. camps (array for campground data, default empty)2. isLoading (loading status, default true)3. errorMsg (error message, default empty string)Use useEffect to fetch data on component mount.After fetching, set isLoading to false.Render logic: If loading, show "Loading data..."; if error, show error; else display data with JSON.stringify.
๐ค AI's Perfect State Management:
import React, { useState, useEffect } from 'react';
import { getCampgrounds } from './api';
export default function CampList() {
// State trio
const [camps, setCamps] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [errorMsg, setErrorMsg] = useState("");
// Empty dependency array means "run once on mount"
useEffect(() => {
const fetchData = async () => {
// 1. Start loading
setIsLoading(true);
// 2. Call our Supabase API
const data = await getCampgrounds();
// 3. Check if data exists
if (data && data.length > 0) {
setCamps(data);
} else {
setErrorMsg("No campground data or connection error.");
}
// 4. Always disable loading state
setIsLoading(false);
};
fetchData();
}, []);
// Conditional Rendering
if (isLoading) {
return <div className="p-10 text-center text-xl text-blue-400 animate-pulse">โณ Loading data...</div>;
}
if (errorMsg) {
return <div className="p-10 text-center text-red-500">โ {errorMsg}</div>;
}
// Success! Temporarily display raw data
return (
<div className="p-10">
<h2 className="text-2xl font-bold mb-4">Raw Database Data</h2>
<pre className="bg-zinc-900 p-4 rounded text-green-400 overflow-auto">
{JSON.stringify(camps, null, 2)}
</pre>
</div>
);
}
๐จ Practice 4: Transforming Raw Data into Beautiful Cards
Now you see green JSON raw data on screen.
We'll combine .map() from last chapter to transform each data item into a stunning GlowCard.
๐ก Vibe Prompt Practice 4: Array Map & Component Rendering
[!IMPORTANT] Copy and send this Prompt to AI:
The camps array contains objects like { id: 1, name: "Starry Camp", price: 1500, location: "Miaoli" }.Modify <CampList>'s final return.1. Use <div className="grid grid-cols-1 md:grid-cols-2 gap-6"> as wrapper.2. Use camps.map() loop.3. Pass each item to <GlowCard> from last chapter.4. Display name (h3), location (p), and price (p, formatted as NT$ xxx / night).Remember React list key prop.
๐ค AI's Finishing Touch:
// Replace the <pre> JSON output with this:
return (
<div className="max-w-6xl mx-auto p-10">
<h2 className="text-3xl font-bold text-white mb-8">๐๏ธ Explore Popular Campgrounds</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{/* Map each camp into a GlowCard */}
{camps.map((camp) => (
// key is required for React's list rendering
<GlowCard key={camp.id}>
<h3 className="text-2xl font-bold text-white mb-2">{camp.name}</h3>
<p className="text-zinc-400 mb-4 flex items-center gap-2">
๐ {camp.location}
</p>
<div className="mt-auto pt-4 border-t border-white/10">
<span className="text-emerald-400 font-bold text-xl">NT$ {camp.price}</span>
<span className="text-zinc-500 text-sm ml-1">/ night</span>
</div>
</GlowCard>
))}
</div>
</div>
);
๐ Deep Dive: See? This is full-stack development from backend to frontend!
- You created a Supabase Table.
- You fetched data with
supabaseClient. - You stored it in
useStateand handled Loading. - You transformed data into glowing HTML with
.map().
After these four steps, your site gains real business value! Now when your boss adds new products via backend forms, your frontend automatically displays beautiful cardsโno code changes needed!
๐ซ Ultimate Pitfall Guide: Infinite Loop Again? (useEffect Trap)
In Vibe Coding, the most common browser crash cause is incorrect useEffect dependency arrays.
๐ก Vibe Prompt Practice 5: Fixing Infinite useEffect Triggers
[!IMPORTANT] Copy and send this Prompt to AI:
My React useEffect fetches Supabase data and updates setCamps.But Chrome's Network tab shows hundreds of API requests per second! Supabase warns me about exceeding free tier!My code:useEffect(() => {fetchData();});What's wrong? How to fix?
๐ค AI's Emergency Rescue:
/*
โ Critical Error:
You forgot the dependency array ([]) at useEffect's end!
Without it, useEffect defaults to "run after every render".
This creates a deadly chain reaction:
1. useEffect fetches data.
2. setCamps triggers re-render.
3. Re-render triggers useEffect again (back to step 1).
This is the infamous "React Infinite Render Loop"! You burned your free API quota in minutes!
โ
Fix:
Always add empty array [] to useEffect, meaning "run only once on mount".
*/
// Corrected code:
useEffect(() => {
fetchData();
}, []); // ๐ This empty array saves lives!
โ Chapter Summary & Debugging Mindset
In this chapter, you evolved from a UI developer to a full-stack engineer!
- Security First: Always keep
VITE_SUPABASE_URLin.env. - Async Fetching: Use
async/awaitwithsupabase.from().select(). - UX Matters: Never show blank screens! Use
isLoadingfor friendly prompts. - Data Transformation: Use
.map()to inject raw JSON into advanced components.
So far, we've only viewed the site on desktop. But 80% of users browse on mobile! Next chapter tackles every developer's nightmare: Chapter 13: RWD & Magic Hamburger Menu. We'll teach you Tailwind techniques to make your site responsive instantly!