codex-lv3-may-2025

React + Supabase Potluck App Project Guide

Welcome! You’re about to build your first full-stack React application with a real database. By the end of this project, you’ll have created a potluck meal management app that can store, display, and manage data - just like the apps you use every day!

What you’ll learn:

How this works:

Each level includes:


Level 1: Planning

Goal: Plan your potluck app using the activity guide.

User Story: As a developer, I want to plan my database structure and components so that I can build my potluck management app efficiently.


📋 Before You Start

Complete the Activity Guide first! Work through Steps 1-4 of the Activity Guide to:

This planning will help you build your app more efficiently!


What You’ll Do

Take time to plan your potluck app using the structured activity guide.

Instructions

Key Planning Questions:

Time Investment: Spend 15-20 minutes on planning - it will save you hours of debugging later!

✅ Check

  1. You have reviewed the activity guide
  2. You understand the project requirements
  3. You have planned your database structure
  4. You have designed your component architecture
  5. You have set clear success criteria

Attribution: This project guide was created with assistance from Claude AI (Anthropic).


Level 2: Project Setup

Goal: Set up your development environment with Vite and React.

User Story: As a developer, I want to create a new React project and install dependencies so that I can start building my potluck management app.


What You’ll Do

Create a new React project using Vite and install the Supabase client library.

Instructions

See here for installation details: Install Supabase Client

💡 Code Hints

Need help with project setup? Check out these snippets:

Show Me: Creating the project
npm create vite@latest practice-with-db -- --template react
cd practice-with-db
npm install
Show Me: Installing Supabase
npm install @supabase/supabase-js
Show Me: Project structure
practice-with-db/
├── src/
│   ├── components/
│   │   └── PotluckMeals.jsx
│   ├── utils/
│   │   └── supabase.js
│   ├── App.jsx
│   └── main.jsx
├── package.json
└── README.md

🔍 Diving Deeper

Why do we need these libraries?

Where do these libraries go?

When you run npm install, npm:

  1. Downloads the libraries from the npm registry
  2. Stores them in the node_modules/ folder in your project
  3. Updates your package.json file to record the dependencies

How to verify installation:

  1. Check package.json: Open your package.json file and look for the dependencies section:
    {
      "dependencies": {
        "@supabase/supabase-js": "^2.x.x",
        "tslib": "^2.x.x"
      }
    }
    
  2. Check node_modules/: Look in your project folder for a node_modules/ directory. Inside, you should see folders named @supabase and tslib.

  3. Verify in terminal: Run npm list to see all installed packages and their versions.

✅ Check

  1. Run npm run dev to start your development server
  2. Open your browser to http://localhost:5173
  3. You should see the default Vite + React welcome page
  4. Check that @supabase/supabase-js is listed in your package.json dependencies
  5. Open your project folder in VS Code and verify you can see the basic file structure


Level 3: Database Setup

Goal: Configure your Supabase database with tables and security policies.

User Story: As a developer, I want to set up my database tables and security policies so that I can store and retrieve potluck data securely.


What You’ll Do

Follow the Supabase setup guides to create your database tables and configure access policies.

Instructions

⚠️ Important Note: Do NOT use the starter code for App.jsx from the Supabase setup guide. That code puts everything in one file, but we’ll be organizing our app into separate components. Follow the component structure outlined in Levels 4-6 below instead.

Show Me: Sample data in database Sample data in database

🔍 Diving Deeper

What are Row Level Security (RLS) policies?

Understanding our policies:

-- Read policy: Allow everyone to read meals
CREATE POLICY "Enable read access for all users" ON potluck_meals
FOR SELECT USING (true);

-- Insert policy: Allow everyone to add meals  
CREATE POLICY "Enable insert for all users" ON potluck_meals
FOR INSERT WITH CHECK (true);

Environment variables explained:

VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key-here

📺 Learn More:

✅ Check

  1. Your Supabase project is created and accessible
  2. The potluck_meals table exists with the correct columns
  3. You have sample data in your table
  4. RLS policies are enabled and configured
  5. Environment variables are set up in your React project
  6. You can see your data in the Supabase dashboard

📚 Vocabulary



Level 4: Create Basic Component Structure

Goal: Create the main PotluckMeals component and import it into your App component.

User Story: As a developer, I want to create a component that can fetch and display data from my database so that I can see my potluck meals.


What You’ll Do

Create the PotluckMeals component with basic structure and import it into your App component.

Instructions

💡 Code Hints

Need help with component structure? Check out these snippets:

Show Me: PotluckMeals component

import { useState } from "react"
import supabase from "../utils/supabase"

export default function PotluckMeals() {
    const [meals, setMeals] = useState([])

    return <>
        <h1>Potluck meals</h1>
        <button>Fetch Meals</button>
        <ul>
            {/* Meals will be displayed here */}
        </ul>
    </>
}
Show Me: App.jsx import
import PotluckMeals from './components/PotluckMeals'

function App() {
  return (<>
    
  </>)
}

export default App

✅ Check

  1. Your component renders without errors
  2. You see “Potluck meals” heading
  3. You see a “Fetch Meals” button
  4. No console errors about missing imports
  5. The component structure is clean and organized


Level 5: Create Button and Handler (Console Log)

Goal: Add a fetch meals function with console logging.

User Story: As a developer, I want to create a button handler so that I can test the connection to my database.


What You’ll Do

Add the fetch meals function with console logging and connect it to your button.

Instructions

💡 Code Hints

Need help with the button handler? Check out these snippets:

Show Me: fetch function
async function handleFetchMeals() {
    console.log("Fetching meals...")
    // We'll add the actual fetch logic in the next step
}
Show Me: button with onClick
<button onClick={handleFetchMeals}>Fetch Meals</button>

✅ Check

  1. Click the “Fetch Meals” button
  2. Check the browser console - you should see “Fetching meals…”
  3. No JavaScript errors occur
  4. The button responds to clicks
  5. Console logging is working correctly


Level 6: Select Data and Console Log

Goal: Update the fetch function to actually retrieve data from Supabase.

User Story: As a developer, I want to fetch data from my database so that I can see what meals are stored.


What You’ll Do

Update the handleFetchMeals function to fetch data from Supabase and log it to console.

Instructions

💡 Code Hints

Need help with data fetching? Check out these snippets:

Show Me: updated fetch function
async function handleFetchMeals() {
    console.log("Fetching meals...")
    const result = await supabase.from("potluck_meals").select()
    const data = result.data
    console.log("Fetched data:", data);
    setMeals(data);
}

🔍 Diving Deeper

What is async/await?

Understanding Supabase queries:

const result = await supabase.from("potluck_meals").select()

Error handling pattern:

Always check for errors when working with databases:

if (result.error) {
    console.error('Database error:', result.error);
    return; // Stop execution if there's an error
}

📺 Learn More:

✅ Check

  1. Click the “Fetch Meals” button
  2. Check the browser console - you should see the fetched data
  3. The meals state is updated with the data
  4. No console errors occur
  5. Data is successfully retrieved from Supabase

📚 Vocabulary



Level 7: Display Data with For Loop

Goal: Display the fetched meals using a for loop.

User Story: As a user, I want to see the meals displayed on the page so that I can view what’s planned for the potluck.


What You’ll Do

Add display logic using a for loop to render each meal in the list.

Instructions

💡 Code Hints

Need help with the display loop? Check out these snippets:

Show Me: display loop
const mealsDisplay = []
for (let i = 0; i < meals.length; i++) {
    mealsDisplay.push(
        <li key={meals[i].id}> 
            {meals[i].meal_name} by {meals[i].guest_name} serves {meals[i].serves} ( {meals[i].kind_of_dish} ) 
        </li>
    )
}
Show Me: displaying the meals
<ul>
    {mealsDisplay}
</ul>
Show Me: Meals displayed in app Meals displayed in app

🔍 Diving Deeper

Why do we need the key prop?

Understanding JSX in loops:

mealsDisplay.push(
    <li key={meals[i].id}> 
        {meals[i].meal_name} by {meals[i].guest_name} serves {meals[i].serves} ( {meals[i].kind_of_dish} ) 
    </li>
)

Alternative approaches:

You could also use .map() instead of a for loop:

const mealsDisplay = meals.map(meal => (
    <li key={meal.id}>
        {meal.meal_name} by {meal.guest_name} serves {meal.serves} ({meal.kind_of_dish})
    </li>
));

📺 Learn More:

✅ Check

  1. Click the “Fetch Meals” button
  2. Your meals should appear in a list below the button
  3. Each meal shows: name, guest, serves count, and dish type
  4. The list updates when you click the button again
  5. If no data appears, check your Supabase connection and RLS policies

📚 Vocabulary



Level 8: Test Data Fetching

Goal: Verify that your data fetching and display is working correctly.

User Story: As a developer, I want to test my data fetching functionality so that I can ensure everything is working properly.


What You’ll Do

Test your data fetching functionality and verify the results.

Instructions

✅ Check

  1. Development server is running without errors
  2. Clicking “Fetch Meals” displays the meals
  3. All meal information is shown correctly
  4. No console errors occur
  5. The functionality works consistently


Level 9: Add Form Structure

Goal: Add a form to your PotluckMeals component for adding new meals.

User Story: As a user, I want to fill out a form to add my dish to the potluck so that others can see what I’m bringing.


What You’ll Do

Add a form with input fields for meal details to your component.

Instructions

💡 Code Hints

Need help with form structure? Check out these snippets:

Show Me: form JSX
// Add this inside your return statement, after the ul element
<div>
    <form onSubmit={handleAddMeal}>
        <label>
            Meal: <input type="text" name="mealName" />
        </label>
        <br/>
        <label>
            Guest: <input type="text" name="guestName" />
        </label>
        <br/>
        <label>
            Serves: <input type="number" name="serves" />
        </label>
        <br/>
        <label>
            Kind of Dish: <input type="text" name="kindOfDish" />
        </label>
        <br/>
        <button type="submit">Add Meal</button>
    </form>
</div>
Show Me: Form added Form added

✅ Check

  1. You see a form with four input fields
  2. Each input has a clear label
  3. The form has a submit button
  4. All input fields have proper name attributes
  5. The form is positioned below the meals list


Level 10: Add Event Handler

Goal: Create the handleAddMeal function to process form submissions.

User Story: As a developer, I want to create a form handler so that I can process the form data when users submit it.


What You’ll Do

Add the handleAddMeal function to your component to handle form submissions.

Instructions

💡 Code Hints

Need help with the form handler? Check out these snippets:

Show Me: form handler
async function handleAddMeal(event){
    event.preventDefault()
    console.log("handle add meal submitted")
    const mealName = event.target.elements.mealName.value
    const guestName = event.target.elements.guestName.value
    const serves = event.target.elements.serves.value
    const kindOfDish = event.target.elements.kindOfDish.value
    
    const newMeal = {
        meal_name: mealName,
        guest_name: guestName,
        serves: parseInt(serves),
        kind_of_dish: kindOfDish
    }
    
    console.log(newMeal)
    // We'll add the insert logic in the next step
}

🔍 Diving Deeper

Understanding form events:

Exploring the event object:

To better understand how events work, try adding this to your form handler:

function handleAddMeal(event) {
    console.log(event); // Examine the entire event object
    console.log(event.target); // Look at the form element
    console.log(event.target.elements); // See all form inputs
    
    event.preventDefault();
    // ... rest of your code
}

What you’ll see in the console:

Form data extraction pattern:

const mealName = event.target.elements.mealName.value
const guestName = event.target.elements.guestName.value

Data type conversion:

serves: parseInt(serves)

Object creation pattern:

const newMeal = {
    meal_name: mealName,
    guest_name: guestName,
    serves: parseInt(serves),
    kind_of_dish: kindOfDish
}

📺 Learn More:

✅ Check

  1. Fill out the form with test data
  2. Submit the form
  3. Check the browser console - you should see “handle add meal submitted”
  4. You should see the newMeal object logged to console
  5. No page refresh occurs when submitting

📚 Vocabulary



Level 11: Create Insert RLS Policy

Goal: Set up a write policy for your potluck_meals table.

User Story: As a developer, I want to configure database security policies so that users can insert new meals into the database.


What You’ll Do

Follow the Supabase Setup Guide to set up a write policy for your potluck_meals table.

Instructions

This allows anyone to create new rows in your table.

🔒 Cybersecurity Reflection: Think about this statement from a security perspective. What are the potential risks of allowing “anyone” to create new rows in your database? Consider:

Note: This policy is intentionally permissive for learning purposes. In production applications, you would implement proper authentication and authorization controls.

Show Me: RLS Policy for inserts RLS Policy for inserts
Show Me: Potential RLS error Potential RLS error

✅ Check

  1. You have created a new RLS policy
  2. The policy allows INSERT operations
  3. The policy uses true in the “with check” statement
  4. The policy is saved and active
  5. You understand the security implications


Level 12: Add Insert Statement

Goal: Update your form handler to insert new meals into the database.

User Story: As a user, I want my form submission to save the meal to the database so that it persists and can be retrieved later.


What You’ll Do

Update your handleAddMeal function to include the database insertion logic.

Instructions

💡 Code Hints

Need help with database insertion? Check out these snippets:

Show Me: updated form handler
async function handleAddMeal(event){
    event.preventDefault()
    console.log("handle add meal submitted")
    const mealName = event.target.elements.mealName.value
    const guestName = event.target.elements.guestName.value
    const serves = event.target.elements.serves.value
    const kindOfDish = event.target.elements.kindOfDish.value
    
    const newMeal = {
        meal_name: mealName,
        guest_name: guestName,
        serves: parseInt(serves),
        kind_of_dish: kindOfDish
    }
    
    console.log(newMeal)
    
    // Insert the new meal
    await supabase.from("potluck_meals").insert(newMeal)
}
Show Me: Verify insert in Supabase Verify insert in Supabase

🔍 Diving Deeper

Understanding CRUD operations:

Database insertion explained:

await supabase.from("potluck_meals").insert(newMeal)

Exploring network requests:

To see what’s actually being sent to Supabase, open your browser’s Developer Tools:

  1. Open Developer Tools: Press F12 or right-click → “Inspect”
  2. Go to Network Tab: Click on the “Network” tab
  3. Submit your form: Fill out and submit the form
  4. Look for the request: You’ll see a request to your Supabase URL
  5. Examine the data: Click on the request to see:
    • Request Payload: The data being sent (your newMeal object)
    • Response: What Supabase sends back
    • Headers: Authentication and other metadata

What you’ll see:

Data persistence:

Error handling considerations:

In production apps, you’d want to handle potential errors:

const { data, error } = await supabase.from("potluck_meals").insert(newMeal)
if (error) {
    console.error('Insert failed:', error);
    // Show user-friendly error message
} else {
    console.log('Meal added successfully:', data);
    // Update UI to show success
}

Database constraints:

📺 Learn More:

✅ Check

  1. Fill out the form with new meal data
  2. Submit the form
  3. Check your Supabase dashboard - the new meal should appear
  4. No console errors occur during insertion
  5. The data is successfully saved to the database

📚 Vocabulary



Level 13: Test Insert and Verify in Supabase Dashboard

Goal: Verify that your insert functionality is working correctly.

User Story: As a developer, I want to verify that my data insertion is working so that I can ensure the database is being updated properly.


What You’ll Do

Test your insert functionality and verify the results in the Supabase dashboard.

Instructions

✅ Check

  1. Form submission completes without errors
  2. New meal appears in Supabase dashboard
  3. All form fields are saved correctly
  4. Data types are preserved (integer for serves)
  5. Multiple submissions work correctly


Level 14: Select Data Back Out and Update List

Goal: Refresh the meals list after inserting a new meal.

User Story: As a user, I want to see my new meal appear in the list immediately after submitting the form so that I can confirm it was added.


What You’ll Do

Update your handleAddMeal function to refresh the meals list after inserting.

Instructions

💡 Code Hints

Need help with refreshing the list? Check out these snippets:

Show Me: updated form handler with refresh
async function handleAddMeal(event){
    event.preventDefault()
    console.log("handle add meal submitted")
    const mealName = event.target.elements.mealName.value
    const guestName = event.target.elements.guestName.value
    const serves = event.target.elements.serves.value
    const kindOfDish = event.target.elements.kindOfDish.value
    
    const newMeal = {
        meal_name: mealName,
        guest_name: guestName,
        serves: parseInt(serves),
        kind_of_dish: kindOfDish
    }
    
    console.log(newMeal)
    
    // Insert the new meal
    await supabase.from("potluck_meals").insert(newMeal)
    
    // Refresh the meals list
    const response = await supabase.from("potluck_meals").select()
    const data = response.data
    setMeals(data)
}
Show Me: Display meals after submit Display meals after submit

✅ Check

  1. Submit a new meal through the form
  2. The meals list updates automatically
  3. Your new meal appears in the list
  4. The list shows all meals including the new one
  5. No manual refresh is needed


Level 15: Clear Inputs After Submit

Goal: Clear the form inputs after successful submission.

User Story: As a user, I want the form to clear after I submit it so that I can easily add another meal.


What You’ll Do

Update your handleAddMeal function to clear the form inputs after submission.

Instructions

💡 Code Hints

Need help with clearing inputs? Check out these snippets:

Show Me: complete form handler with clearing
async function handleAddMeal(event){
    event.preventDefault()
    console.log("handle add meal submitted")
    const mealName = event.target.elements.mealName.value
    const guestName = event.target.elements.guestName.value
    const serves = event.target.elements.serves.value
    const kindOfDish = event.target.elements.kindOfDish.value
    
    const newMeal = {
        meal_name: mealName,
        guest_name: guestName,
        serves: parseInt(serves),
        kind_of_dish: kindOfDish
    }
    
    console.log(newMeal)
    
    // Insert the new meal
    await supabase.from("potluck_meals").insert(newMeal)
    
    // Refresh the meals list
    const response = await supabase.from("potluck_meals").select()
    const data = response.data
    setMeals(data)
    
    // Clear the form inputs
    event.target.elements.mealName.value = ""
    event.target.elements.guestName.value = ""
    event.target.elements.serves.value = ""
    event.target.elements.kindOfDish.value = ""
}
Show Me: Clear inputs after submit Clear inputs after submit

✅ Check

  1. Submit a new meal through the form
  2. All form inputs clear automatically
  3. The form is ready for the next entry
  4. The meals list still updates correctly
  5. You can immediately add another meal


Level 16: Add Select Dropdown (Challenge)

Goal: Replace the text input for dish type with a dropdown for better data consistency.

User Story: As a user, I want to select from predefined dish types so that the data is consistent and easier to filter.


What You’ll Do

Replace the “Kind of Dish” text input with a select dropdown containing predefined options.

Instructions

💡 Code Hints

Need help with select dropdown? Check out these snippets:

Show Me: select dropdown
<label>
    Kind of Dish:
    <select name="kindOfDish" defaultValue="">
        <option value="" disabled>Select a kind</option>
        <option value="entree">Entree</option>
        <option value="side">Side</option>
        <option value="snack">Snack</option>
        <option value="dessert">Dessert</option>
        <option value="drink">Drink</option>
    </select>
</label>
Show Me: Option select dropdown Option select dropdown

✅ Check

  1. The text input is replaced with a dropdown
  2. You can select from the predefined options
  3. The form still submits correctly
  4. Data is saved with the selected value
  5. The dropdown resets after form submission


Level 17: Create Beverages Table and Component

Goal: Create another table and component for Beverages following the same pattern as meals.

User Story: As a developer, I want to create additional tables and components so that I can manage different types of potluck items.


What You’ll Do

Create a beverages table and component following the same pattern as the meals component.

Instructions

✅ Check

  1. The beverages table exists in your Supabase dashboard
  2. RLS policies are configured for the beverages table
  3. The Beverages component renders without errors
  4. You can fetch and display data from the beverages table
  5. The form works for adding new beverages


Level 18: Create Utensils Table and Component

Goal: Create a table and component for Utensils following the same pattern.

User Story: As a developer, I want to create additional tables and components so that I can manage different types of potluck items.


What You’ll Do

Create a utensils table and component for managing potluck utensils.

Instructions

✅ Check

  1. The utensils table exists in your Supabase dashboard
  2. RLS policies are configured for the utensils table
  3. The Utensils component renders without errors
  4. You can fetch and display data from the utensils table
  5. The form works for adding new utensils


Level 19: Bonus Challenges ⚡

CHALLENGE LEVEL

User Story: As a developer, I want to implement advanced features so that I can create a more impressive and functional app.


What You’ll Do

Choose from these advanced challenges to extend your potluck app.

Challenge Options

Complete at least 2 of the following challenges:

Challenge 1: Select Dropdown

Remember that Level 16 (select dropdown) is also a challenge that you can implement!

Challenge 2: Creative Table

Create another table that you think would improve your app (e.g., dietary restrictions, allergies, etc.). Decide on the columns yourself.

Challenge 3: Styling

Style your app using CSS or Bootstrap to make it more visually appealing.

Challenge 4: Conditional Styling

Add conditional styling based on the type of dish or other data.

Challenge 5: Creative Display

Use elements other than <li> tags to display your items (cards, tables, etc.).

Challenge 6: Component Breakdown

Break your app into smaller, reusable components with props.

Challenge 7: File Upload

Have an option to upload a file. Consider using Cloudinary and an “upload url”. This will take some research.

✅ Check

  1. Choose at least 2 challenges to implement
  2. Test your new features thoroughly
  3. Update your README with new features
  4. Commit your changes with descriptive messages
  5. Consider adding screenshots or GIFs


Level 20: Project Complete! 🎉

Congratulations! You’ve successfully built a complete React + Supabase potluck management app!

What You’ve Accomplished

You’ve built a fully functional app that demonstrates:

Skills You’ve Developed

Through this project, you’ve gained hands-on experience with:

Next Steps

After completing this project, consider exploring:

Resources

Share Your Work!

Consider:


🎊 Well Done!

You’ve completed a significant milestone in your React and database journey. Your potluck app demonstrates your ability to build full-stack web applications with modern tools.

Keep building, keep learning, and keep growing as a developer!

Project Status: Complete!


Troubleshooting Guide

Common Issues and Solutions

Environment Variables Not Loading: Make sure your .env.local file is in the project root and variables start with VITE_

RLS Policy Errors: Check SQL syntax and ensure policies are properly quoted

CORS Issues: Verify your Supabase URL and keys are correct

Form Not Submitting: Ensure you have event.preventDefault() in your handler

Getting Help:


Attribution: This project guide was created with assistance from Claude AI (Anthropic).