Day 1 of 5
⏱ ~50 minutes
React + AI — Day 1

React Components and State: The Foundation

You can't add AI to a React app you don't understand. Day 1 builds the foundation fast: components, props, useState, and event handlers. By the end you'll have a working React app ready for the AI layer.

Create a React App With Vite

Vite is significantly faster than Create React App. Use it for all new projects.

bash
npm create vite@latest my-ai-app -- --template react
cd my-ai-app
npm install
npm run dev    # opens http://localhost:5173
text — project structure
my-ai-app/
├── src/
│   ├── App.jsx          ← root component
│   ├── main.jsx         ← entry point
│   └── components/      ← your components go here
├── public/
├── index.html
└── vite.config.js

Components — The Building Blocks

A React component is a function that returns JSX (HTML-like syntax). Components are reusable — write once, use anywhere.

jsx
// src/components/MessageCard.jsx
function MessageCard({ role, content, timestamp }) {
  return (
    
{role} {timestamp}

{content}

); } export default MessageCard; // Usage in App.jsx import MessageCard from './components/MessageCard'; function App() { return (
); }
ℹ️
JSX looks like HTML but it's JavaScript. Key differences: use className instead of class, self-close all tags (<img />), and wrap expressions in {}.

useState — Adding Interactivity

jsx
import { useState } from 'react';

function ChatInput({ onSend }) {
  const [message, setMessage] = useState('');
  const [isLoading, setIsLoading] = useState(false);

  function handleSubmit(e) {
    e.preventDefault();
    if (!message.trim()) return;
    onSend(message);
    setMessage('');  // clear after sending
  }

  return (
    
setMessage(e.target.value)} placeholder="Ask anything..." disabled={isLoading} />
); }

Lists and Keys

jsx
function MessageList({ messages }) {
  return (
    
{messages.map((msg) => ( ))}
); }

Building the Chat Shell

jsx — src/App.jsx
import { useState } from 'react';

function App() {
  const [messages, setMessages] = useState([
    { id: 1, role: 'assistant', content: 'Hi! Ask me anything.' }
  ]);

  function handleSend(text) {
    // Add user message
    const userMsg = { id: Date.now(), role: 'user', content: text };
    setMessages(prev => [...prev, userMsg]);

    // We'll add the AI response in Day 2
    const aiMsg = {
      id: Date.now() + 1,
      role: 'assistant',
      content: `You said: "${text}" — AI coming in Day 2!`
    };
    setMessages(prev => [...prev, aiMsg]);
  }

  return (
    

AI Chat

{messages.map(msg => (
{msg.role}: {msg.content}
))}
); } function ChatInput({ onSend }) { const [text, setText] = useState(''); return (
{ e.preventDefault(); if (text.trim()) { onSend(text); setText(''); } }}> setText(e.target.value)} placeholder="Type a message..." />
); } export default App;
📝 Exercise
Build the Chat Shell
  1. Create the Vite React app and delete the boilerplate content in App.jsx.
  2. Create a MessageCard component that takes role and content as props.
  3. Create a ChatInput component with a controlled text input and submit handler.
  4. In App.jsx, add a messages state array and an onSend handler that adds the user message.
  5. Render the messages list and the input form.
  6. Style it minimally: messages should visually distinguish 'user' vs 'assistant'.

Lesson Summary

  • Components are functions that return JSX. Pass data down via props.
  • useState triggers a re-render when its value changes. Always update state with the setter, never mutate directly.
  • Use the spread pattern [...prev, newItem] to add to an array in state.
  • Every item in a list needs a unique key prop — use IDs, not array indexes if possible.
Challenge

Add a 'Clear Chat' button that resets messages to just the initial greeting. Add a message counter badge in the header showing how many messages are in the conversation.

Finished this lesson?