MULT-128

Web Development 4

Credits: 3 Hours: 45 Semester: 3 Corequisite: MULT-114 Tools: VS Code, React, Vite, Cloudflare Pages

You know HTML, CSS, and JavaScript. You can build pages, style them, and add interactivity. But as your projects grow beyond a few pages, the vanilla approach starts to break down. You end up with tangled event listeners, duplicated HTML blocks, and state that's impossible to track. That's the problem frameworks solve.

React is the most widely used frontend framework in the world. It powers Instagram, Airbnb, Netflix, Discord, and millions of other applications. Learning React doesn't just make you a better developer. It makes you employable, and it gives you the tools to build genuinely interactive web applications that feel as smooth as native apps. This course covers the fundamentals: components, state, props, routing, and building a real project from start to deployment.

1
Introduction to React
โ–ถ

Before you write a single line of React code, you need to understand why it exists. React solves real problems that every frontend developer hits eventually. If you don't understand the problems, the solutions will feel arbitrary and confusing.

Why Frameworks Exist

Imagine building a social media feed in plain JavaScript. Every time a user likes a post, you need to find the right button in the DOM, update its text, maybe change its color, update a counter somewhere else on the page, and make sure the state stays in sync if the user scrolls away and comes back. Now multiply that by fifty interactive elements on one page. It becomes a nightmare of document.querySelector calls, event listeners, and manual DOM updates that constantly fall out of sync.

Frameworks like React flip the model. Instead of telling the browser how to update the page step by step (imperative), you describe what the page should look like for a given state (declarative). React figures out what changed and updates the DOM for you. You say "when the like count is 5, render this" and React handles the transition from 4 to 5 automatically.

Components: The Building Blocks

React applications are built from components. A component is a reusable piece of UI that manages its own content and behavior. Think of it like a custom HTML element you design yourself.

A navigation bar is a component. A user profile card is a component. A button can be a component. Your entire page is a component made up of smaller components. This composition model is powerful because you build complex UIs from simple, testable, reusable pieces.

In modern React, components are just JavaScript functions that return what looks like HTML. Here's the simplest possible component: a function called Greeting that returns a paragraph tag saying "Hello, world!" This function-based approach replaced the older class-based components. Every tutorial and job from 2020 onward uses function components, so that's what we'll learn.

JSX: HTML in Your JavaScript

That HTML-looking syntax inside your component function isn't actually HTML. It's called JSX (JavaScript XML), and it's a syntax extension that lets you write markup directly in your JavaScript files. Under the hood, JSX gets transformed into regular JavaScript function calls, but you never need to think about that.

JSX has a few differences from real HTML. You use className instead of class (because "class" is a reserved word in JavaScript). You close all tags, even self-closing ones like <img /> and <br />. You can embed any JavaScript expression inside curly braces: {"Hello, " + name} or {items.length}. This blending of markup and logic feels weird at first but becomes natural fast.

Setting Up with Vite

Vite (pronounced "veet," it's French for "fast") is a modern build tool that creates React projects with zero configuration. It's faster than the older create-react-app tool and has become the standard way to start a new React project.

To create a new project, open your terminal and run: npm create vite@latest my-app -- --template react. This scaffolds a project folder with everything configured. Then cd my-app, npm install, and npm run dev to start a development server with hot reloading. Every time you save a file, the browser updates instantly.

The generated project structure has an src/ folder where your code lives. main.jsx is the entry point that mounts your root component into the DOM. App.jsx is your root component. You'll create new component files alongside these.

๐Ÿ’ก Key Takeaway

React exists to solve the complexity of updating UIs when data changes. Components are reusable functions that return JSX (HTML-like syntax in JavaScript). Vite is the modern way to scaffold a React project. The mental shift from imperative DOM manipulation to declarative components is the hardest part. Once it clicks, everything else follows.

๐Ÿ”จ Exercise 1.1: Scaffold and Explore

Create your first React project with Vite and get familiar with the structure:

  1. Run npm create vite@latest my-portfolio -- --template react in your terminal
  2. Install dependencies with npm install and start the dev server with npm run dev
  3. Open src/App.jsx and replace the boilerplate with a simple component that displays your name and a short bio paragraph
  4. Create a new file src/Header.jsx with a Header component containing a nav with three links. Import and use it inside App.
  5. Verify hot reloading works: change some text and watch the browser update without refreshing

Deliverable: A running Vite + React project with a custom App component that imports and renders a separate Header component.

2
State & Props
โ–ถ

Components need data. A profile card needs a user's name and photo. A counter needs a number that changes when you click a button. React handles data in two distinct ways: props (data passed into a component from its parent) and state (data managed inside a component that can change over time). Understanding the difference is the single most important concept in React.

Props: Data Flowing Down

Props are how parent components pass data to child components. They work like HTML attributes. When you write <ProfileCard name="Kiarra" role="Creator" />, you're passing two props: name and role. Inside the ProfileCard component, you receive them as an object parameter and use them in your JSX.

Props are read-only. A child component should never modify the props it receives. This one-way data flow is a core React principle. Data flows down from parent to child, never the other direction. If a child needs to communicate something back up to its parent, the parent passes a function as a prop, and the child calls that function.

You can pass anything as a prop: strings, numbers, arrays, objects, functions, even other components. This flexibility is what makes components truly reusable. A Button component that accepts an onClick prop and a label prop can be used for "Submit," "Cancel," "Delete," or anything else without changing its internal code.

The useState Hook

State is data that lives inside a component and can change in response to user actions. The useState hook is how you create state in function components. It returns an array with two items: the current value and a function to update it.

When you call const [count, setCount] = useState(0), you're creating a state variable called count with an initial value of 0, and a setter function called setCount. When you call setCount(count + 1), React re-renders the component with the new value. You never modify state directly. Always use the setter.

This re-rendering behavior is the heart of React. When state changes, the component function runs again, JSX is recalculated, and React efficiently updates only the parts of the DOM that actually changed. You don't write any DOM manipulation code. You just describe what the UI should look like for the current state, and React handles the transitions.

Lifting State Up

Sometimes two sibling components need to share the same data. For example, a search input and a results list both need access to the search query. Since props only flow downward, the solution is to "lift" the shared state up to the nearest common parent.

The parent component owns the state and passes it down to both children as props. It also passes a setter function down to the child that needs to update the state. When that child calls the setter, the parent re-renders, which re-renders both children with the new data. This pattern feels indirect at first, but it keeps data flow predictable and debuggable.

Controlled Components

In plain HTML, form inputs (text fields, checkboxes, selects) manage their own internal state. The browser tracks what the user has typed. In React, you typically take control of this by making the input's value a piece of React state. This is called a controlled component.

For a text input, you set its value prop to a state variable and its onChange prop to a function that updates that state variable. Every keystroke triggers the onChange handler, which updates state, which re-renders the input with the new value. It sounds circular, but it gives you total control: you can validate input in real time, format it, prevent certain characters, or sync it with other parts of your UI.

Think of props as arguments to a function. Think of state as variables inside a function. Props come from outside; state is managed inside. When state changes, the component re-renders.

๐Ÿ’ก Key Takeaway

Props flow down from parent to child and are read-only. State lives inside a component and triggers re-renders when updated via its setter function. Lift state up to the nearest common parent when siblings need to share data. Controlled components tie form inputs to React state for full control over user input.

๐Ÿ”จ Exercise 2.1: Build a Contact Form with State

Build a contact form component that demonstrates state management:

  1. Create a ContactForm.jsx component with three controlled inputs: Name, Email, and Message (textarea)
  2. Use useState for each field (or one state object for all three)
  3. Add a character counter below the Message field that updates in real time as the user types
  4. Add a "Submit" button that, when clicked, displays the form data in a preview section below the form (don't actually send it anywhere)
  5. Add a "Clear" button that resets all fields to empty
  6. Bonus: Disable the Submit button if any field is empty

Deliverable: A working ContactForm component with real-time character counting, form preview on submit, and clear functionality. All inputs must be controlled components.

3
Routing & Navigation
โ–ถ

A real application has multiple pages: a home page, an about page, a portfolio, a contact form. In a traditional website, each page is a separate HTML file and clicking a link triggers a full page reload from the server. In a React application, you build a Single-Page Application (SPA) where navigation happens entirely in the browser, with no reloads, making transitions instant and smooth.

React Router

React Router is the standard routing library for React. Install it with npm install react-router-dom. It provides components that map URL paths to React components, so when the user navigates to /about, React Router renders your About component, and when they go to /portfolio, it renders your Portfolio component, all without a page reload.

The setup involves wrapping your app in a BrowserRouter component, defining your routes with Routes and Route components, and using Link components instead of regular anchor tags for navigation. The Link component prevents the default browser navigation and lets React Router handle the URL change in JavaScript.

A basic setup looks like this: BrowserRouter wraps everything at the top level (usually in main.jsx). Inside your App component, you define Routes containing individual Route elements, each mapping a path to an element (your component). Your navigation uses Link components with to props instead of anchor tags with href attributes.

SPA Navigation

When a user clicks a Link in your React app, several things happen behind the scenes. React Router intercepts the click, updates the browser's URL bar (using the History API), and tells React to render the component associated with the new path. The browser's back and forward buttons work correctly because React Router syncs with the browser's history stack.

This is dramatically faster than traditional navigation because the browser never makes a round trip to the server. The JavaScript bundle containing all your components is already loaded. Switching pages is just swapping which component is rendered, which happens in milliseconds.

One practical consideration: if you want a navigation bar that appears on every page, put it outside the Routes component but inside the BrowserRouter. This way the nav renders on every route without being included in each page component individually.

Dynamic Routes

Many applications have pages that follow a pattern. A blog might have /posts/1, /posts/2, /posts/3 and so on. You don't want to hardcode a route for every single post. Instead, you define a dynamic route with a URL parameter: /posts/:id. The colon indicates a variable segment.

Inside the component that renders for that route, you use the useParams hook to access the parameter. If the user navigates to /posts/42, calling useParams() gives you { id: "42" }. You then use that ID to fetch the right data or display the right content. This pattern is used everywhere: user profiles (/users/:username), product pages (/products/:productId), and project details (/projects/:slug).

Protected Routes

Some pages should only be accessible to logged-in users. A dashboard, account settings, or admin panel shouldn't be visible to random visitors. Protected routes check whether the user is authenticated before rendering the component. If they're not authenticated, you redirect them to a login page.

The common pattern is to create a wrapper component (often called ProtectedRoute) that checks authentication status. If authenticated, it renders the child component. If not, it uses React Router's Navigate component to redirect to /login. You wrap your protected routes with this component in your route definitions.

๐Ÿ’ก Key Takeaway

React Router turns your React app into a multi-page experience without actual page reloads. Use Link instead of anchor tags for navigation. Dynamic routes with URL parameters (:id) handle patterns like blog posts and user profiles. Protected routes check authentication before rendering. Keep shared UI (nav bars, footers) outside the Routes component.

๐Ÿ”จ Exercise 3.1: Add Routing to Your Portfolio

Convert your single-component portfolio into a multi-page React application:

  1. Install React Router: npm install react-router-dom
  2. Create three page components: Home.jsx, Portfolio.jsx, and Contact.jsx
  3. Set up BrowserRouter in main.jsx and define routes for /, /portfolio, and /contact
  4. Build a navigation bar component that uses Link components and appears on every page
  5. Add a dynamic route: /portfolio/:projectId that displays different project details based on the URL parameter
  6. Create at least 3 "project" objects in an array and render the right one based on the :projectId param

Deliverable: A React app with three navigable pages, a persistent nav bar, and a dynamic project detail route that reads from URL parameters.

4
Building a Creator Dashboard
โ–ถ

This module ties everything together. You'll build a real, functional creator dashboard that fetches data from an API, displays it in organized components, includes a working form, and deploys live to Cloudflare Pages. This is the kind of project you'd put in a portfolio or adapt for your own creator business.

Fetching Data from APIs

Most real applications don't have their data hardcoded. They fetch it from APIs (Application Programming Interfaces), which are endpoints that return data, usually in JSON format. React provides the useEffect hook for running side effects like data fetching when a component mounts.

The pattern looks like this: you declare a state variable for your data (starting as an empty array or null), a loading state (boolean), and optionally an error state. Inside a useEffect with an empty dependency array (so it runs once on mount), you call fetch() with the API URL, convert the response to JSON, and set your data state with the result.

While data is loading, you render a loading indicator. If an error occurs, you render an error message. Once data arrives, you map over it to render your components. This loading/error/success pattern is used in virtually every React application. Get comfortable with it because you'll write it dozens of times.

For this course, we'll use free public APIs to simulate real data. The JSONPlaceholder API provides fake posts, users, and comments that are perfect for practice. In a real creator dashboard, you'd replace these with your own API endpoints.

Displaying Content in Components

Once you have data, you need to display it. The React way is to map() over your data array and render a component for each item. Each item component receives the data as props. Remember to add a unique key prop to each item in the list. React uses keys to efficiently update the DOM when items change, are added, or removed.

A creator dashboard might display content cards showing your posts, their view counts, publish dates, and status (published/draft). Each card is a component. The dashboard page fetches the data and maps over it, passing each post's data to a Card component. This separation of concerns keeps your code organized as the application grows.

Forms That Submit Data

You built a contact form in Module 2. Now let's go further. In a real dashboard, forms don't just display data locally. They send it to a server. The process uses fetch() with the POST method instead of GET.

When the user submits the form, you prevent the default form submission with e.preventDefault(), construct a request body from your state, and send it with fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(formData) }). You handle the response, show a success message, and clear the form.

Good form UX includes disabling the submit button while the request is in flight, showing a loading state, handling errors gracefully (network failures, validation errors from the server), and providing clear feedback on success. These details separate a polished application from a prototype.

Deploying to Cloudflare Pages

Deploying a React app to Cloudflare Pages is straightforward. First, build your project by running npm run build. This creates a dist/ folder containing optimized, production-ready static files (HTML, CSS, and JavaScript bundles).

Then go to your Cloudflare dashboard, navigate to Workers & Pages, click Create, choose Pages, and upload the contents of your dist/ folder. Within seconds, your React app is live on a global CDN with HTTPS.

One important detail for SPAs: since React Router handles routing in the browser, you need Cloudflare to serve index.html for all paths. Create a _redirects file in your public/ folder with the content /* /index.html 200. This tells Cloudflare to route all requests through your React app, letting React Router handle the path resolution.

Building something real is the best way to learn. Reading tutorials only takes you so far. The act of debugging a broken fetch call or figuring out why your state isn't updating teaches more than any article.

๐Ÿ’ก Key Takeaway

Real applications fetch data from APIs using useEffect and the fetch API. Map over data arrays to render lists of components with unique keys. Forms submit data with POST requests. Deploy React apps to Cloudflare Pages by uploading the dist/ folder, and add a _redirects file for SPA routing. Build, break, fix, deploy.

๐Ÿ”จ Exercise 4.1: Build and Deploy a Creator Dashboard (Course Deliverable)

This is the capstone project. Build a multi-page React application with real data and deploy it live:

  1. Create a dashboard with at least 3 routes: Home (overview), Content (list of posts), and Add New (form)
  2. Fetch data from JSONPlaceholder's /posts endpoint and display it as content cards on the Content page
  3. Each card should be clickable and link to a dynamic route (/content/:id) showing the full post
  4. Build an "Add New" page with a form (title and body fields) that POSTs to JSONPlaceholder's API and shows a success message
  5. Add loading states and error handling for all data fetching
  6. Style the application with CSS (custom styles, not a framework)
  7. Build and deploy to Cloudflare Pages with proper SPA routing

Deliverable: A live URL on Cloudflare Pages pointing to your deployed React creator dashboard with data fetching, dynamic routes, a working form, and polished styling.

๐Ÿ’ก Course Complete

You now understand React components, state management with hooks, client-side routing, data fetching from APIs, and deploying production applications. These are the fundamentals that every React developer uses daily. Next up: MULT-213 Web Development 5, where you'll dive into backend development and full-stack architecture.

Next Course โ†’
MULT-213: Web Development 5
โ†’