Week 4
Last week, we used Git to save our practice page and review a change. This week, we'll rebuild part of that page with React. By the end, you'll have one InterestCard component used twice with different text, plus a layout that fits a narrow or wide browser. We'll also try a button that changes a card title and follow what happens inside the page.
Why are we using React?
In Week 2, we found a paragraph with querySelector and changed its textContent. That worked because we knew exactly which element needed different text.
Now think about our merch site. Choosing a size can change the price and whether a button is available. Adding an item can change its quantity in the cart and the displayed subtotal. We need those pieces to agree about the current data.
React is a JavaScript library for building user interfaces. A user interface is the part someone sees and uses, such as a product card or a size selector. With React, we describe what those parts should show for the current data. When that data changes through React, React calculates the next result and updates the browser's elements.
We divide the interface into components. A component is a reusable piece of the interface defined in code. In this lesson, our component is a JavaScript function that returns a description of one interest card. We can use it twice and give each card different information. Later, a ProductCard can work the same way for merch.
We can build this with plain JavaScript too. React gives us a common structure for the repeated, changing parts that our teams will work on. It also adds concepts, dependencies, and build tools. It does not make a page faster just because we used it. For a short information page with one button, I would usually keep the plain HTML, CSS, and JavaScript version.
You'll hear React called declarative. In this context, that means we describe the result we want for the current data. Compare that with Week 2, where we wrote the instruction to replace a particular paragraph's text. Both approaches produce ordinary browser elements.
| Job | Earlier page | React page |
|---|---|---|
| Describe a card | Write HTML. | Return JSX from a component. JSX is HTML-like syntax inside JavaScript. |
| Style the card | Write CSS. | Keep writing CSS. Use className in JSX where HTML uses class. |
| Reuse the card | Copy markup or write a JavaScript function. | Use one component several times with different inputs, called props. |
| Change what is displayed | Find and change DOM elements. | Update state and let React calculate the result. State is information React remembers between renders. |
| Run the project | Open an HTML file. | Run Vite so it can process JSX and serve the files. |
Before continuing, name one part of a merch page that repeats and one value that changes. A product card repeats. The selected size changes. Explain one cost of adding React as well.
What is an element, and what is the DOM?
Start with the heading from our interest card:
<h3>Video games</h3>
<h3> is the opening tag. Video games is the content. </h3> is the closing tag. Together, they describe an HTML heading element. A tag is one part of the markup for an element. An element such as img has no content or closing tag in HTML.
This picture labels the same parts of a paragraph:
Image from MDN's Basic HTML syntax, Mozilla Contributors, CC BY-SA 2.5. Displayed without alteration.
The browser turns HTML into a set of objects that represent the page. This is the DOM, or Document Object Model. JavaScript can find these objects, read them, and change them.
A node is one item in the DOM. It is a general name for several kinds of object. Two kinds matter in this example:
- An element node represents an element such as
h3orp. - A text node holds the words inside an element, such as
Video games.
For <h3>Video games</h3>, the browser makes an h3 element node and a separate text node holding Video games. The opening and closing tags describe the same element. They do not become two separate nodes.
Here is the whole card:
<article class="interest-card">
<h3>Video games</h3>
<p>I like learning how games are designed.</p>
</article>
The article contains h3 and p directly. We call article their parent, and h3 and p its children. The h3 is also the parent of the text node holding Video games. A child can have children of its own.
When we draw these connections, they branch. That is why we call this structure a tree. Each box below is one node. Each line connects a parent to a child:
Wiki image placement: Upload dom-interest-card.png and insert it here as a full-width image. Use this description: "The article contains h3 and p element nodes. Each has a separate text node. Changing the heading text keeps the h3 element." Then remove this editor note.
Three of the shown nodes are elements: article, h3, and p. The other two are text nodes. Every DOM element is a node because an element is one kind of node. A text node is also a node, but it is not an element.
The browser also makes text nodes for spaces and line breaks between tags. We leave those out of the picture so the card's structure is easier to see. You can read more in MDN's Node reference, but the two kinds above are enough for this exercise.
Week 2's querySelector returned a DOM element. For example, put the card markup above in a plain HTML page. After the card, add a script element containing this JavaScript, as we did in Week 2:
const heading = document.querySelector(".interest-card h3");
heading.textContent = "Board games";
The selector finds the first h3 inside an element with the class interest-card. heading refers to that live element. Assigning its textContent changes the text inside it. The browser shows "Board games" and keeps the paragraph. The saved HTML file on disk still says "Video games". Changing the live page does not rewrite that file.
Point to what changed in the diagram. The text inside h3 now says "Board games". The h3 element stays in place, and the paragraph keeps its words.
Cover the diagram for a moment. In <h3>Video games</h3>, which node is the element, and which holds the words? The element node is h3. Its text child holds Video games. Keep that distinction in mind when we later compare a DOM element with a React element.
Starting the React page
Open the whole practice-page repository in desktop VS Code. The React starter belongs in practice-page/react-page. If that folder is missing, use Creating the starter near the end of this page. That section includes every file you need. If you already have the starter, open a terminal in react-page and run npm install once before continuing.
Open the integrated terminal. The prompt usually shows the folder where commands will run; this is the current folder. Start at the top of practice-page. If you are inside react-page, run cd .. once: cd changes the current folder, and .. means its parent folder. Then start a branch for this lesson:
git switch main
git pull
git status
git switch -c react-profile
Before the last command, git status should show no unfinished changes from earlier lessons. The new react-page folder may be listed if you have not saved it with Git yet. If it lists an unfinished change from another lesson, stop and ask for help.
Now move into the React folder and start the page. cd react-page enters that folder from practice-page:
cd react-page
npm run dev
The terminal will print a local address. It often starts with http://localhost:, followed by a port number. Open the exact address shown on your computer.
This command starts a development server. The server gives the browser the files React needs and watches them while we work. Keep this terminal running. When you save a React file, the browser should update.
The address is local to your computer. Running the development server does not publish the page online.
How this project reaches the browser
The starter supplies a few files that connect our component to the page. Open them once so you know what they do. We will mainly edit App.jsx, the new InterestCard.jsx, and CSS today.
| File | What it does in this starter |
|---|---|
index.html |
Contains a div with id="root" and loads src/main.jsx. This is inside react-page, separate from the original HTML lesson. |
src/main.jsx |
Imports App and styles.css, then asks React to display App inside that root element. Leave its startup code in place. |
src/App.jsx |
Defines the component describing our practice page. This is where we will first change the heading. |
src/styles.css |
Contains ordinary CSS for the page and cards. |
public/avatar.svg |
Contains the supplied image. Vite serves it at /avatar.svg. |
package.json |
Lists the project's packages and names commands such as dev and build. |
Follow the path from index.html to main.jsx to App.jsx. React uses the description returned by App to create the elements inside the root. The page still has HTML elements that you can inspect in the browser.
React and Vite have different jobs. React describes and updates the interface. Vite is the development and build tool in this starter; it processes JSX and serves the project locally. Node.js runs those tools on your computer, and npm installs packages and runs the named scripts. In this project, Node.js is not the shopper's browser or a custom store backend.
Saving App.jsx updates the local browser because of the development tools. That automatic update while editing is different from an event changing state while someone uses the finished page.
React and JSX
We will write our components as JavaScript functions that return a description of part of the page. return gives a result back to the code that called the function. React calls our component and uses that result to decide what to show. Our Week 2 function changed an existing paragraph; this function gives React a description of the page.
Open react-page/src/App.jsx. App is the main component in this project. The .jsx ending tells us that the file contains JavaScript and JSX.
JSX lets us write HTML-like markup inside JavaScript. Most of the elements in App.jsx should look familiar from Week 1. There are a few differences:
- JSX uses
classNameto assign a CSS class. A class is a label we can reuse on several elements to give them the same styles. Plain HTML calls this attributeclass. - Every tag must close. The image ends with
/>. - The JSX in this starter returns one outer element:
main. - Braces place a JavaScript value inside the markup.
The elements still have their HTML jobs. Our h1, h2, and h3 describe the heading order, article groups one card, and alt describes the image. Keep the section's aria-labelledby connected to the heading's id. React does not make that work for us if we remove it. Attributes beginning with aria- or data- keep their dashes in JSX.
For an action, use a real button. The browser already knows how to focus it and activate it from the keyboard. Moving HTML into a component should preserve those behaviors.
Find this line:
<h1>Hello, I'm Alex</h1>
Change Alex to your name or a nickname. Save the file and look at the browser. The heading should update without a manual refresh.
The development server saw the saved change and sent the updated page to the browser. The saved code is still in App.jsx, just as our earlier page content was saved in index.html.
Making a component
The interest card is currently written inside App. We'll move it into its own component. The page should look the same after the move.
Create a file named InterestCard.jsx inside react-page/src. Add this code:
export default function InterestCard() {
return (
<article className="interest-card">
<h3>Video games</h3>
<p>I like learning how games are designed.</p>
</article>
);
}
InterestCard returns the JSX between the parentheses after return. The parentheses let that result span several lines. Keep the opening parenthesis on the same line as return, as shown. Component names start with a capital letter. Lowercase names such as article, h3, and p refer to HTML elements.
The words export default make this component available to another file.
Return to App.jsx. Remove the matching article from inside interest-list. At the top of the file, before the App function, add:
import InterestCard from "./InterestCard.jsx";
import brings the component from InterestCard.jsx into this file. The ./ means the two files are in the same folder.
Inside interest-list, where the article used to be, add:
<InterestCard />
Save both files. The card should return and look the same as it did before. <InterestCard /> tells React to use the component we made.
We moved the card without changing its result. Giving the card its own component lets us reuse it.
Passing props
If we add <InterestCard /> twice now, both cards will show the same words. Props let one component give information to another component.
App uses InterestCard, so App is the parent component and InterestCard is its child component. The parent passes props to the child.
Replace the first line of InterestCard.jsx so the function receives a value named props:
export default function InterestCard(props) {
props is the function's parameter: the name we use for the information it receives. React puts the values passed to a component into the props object. An object groups values under names. We will use the names title and description.
Replace the fixed heading and paragraph inside the component:
export default function InterestCard(props) {
return (
<article className="interest-card">
<h3>{props.title}</h3>
<p>{props.description}</p>
</article>
);
}
The braces tell JSX to display a JavaScript value. props.title gets the value named title, and props.description gets the value named description.
The card will be blank until App passes those values. In App.jsx, replace <InterestCard /> with:
<InterestCard
title="Video games"
description="I like learning how games are designed."
/>
Save both files. The original card should appear again.
The prop name must match in both places. title="Video games" passes the value. props.title reads it, and {props.title} displays it.
Treat props as read-only inputs. To change this card's title, change the value that App passes. Do not assign a new value to props.title inside InterestCard.
Quoted text or a JavaScript value?
What would title="title" display? It passes the literal word "title". Quotes do not ask JavaScript to look up a variable.
For a quick comparison, add this inside App, above its return:
const title = "Board games";
Change the first card to use title={title}. The braces read the variable, so the card now receives "Board games". The same rule works with description, an image's src, and other props. Braces can contain an expression that produces a value, such as a variable or a calculation.
Try title="title", then title={title}, and compare the headings. Restore the original title prop and remove the temporary variable before continuing. The reference keeps the original fixed strings.
What happens if React calls the component again?
If title and description are the same, InterestCard should describe the same card each time. React may call a component again while working out an update. We should be able to do that without changing data elsewhere in the program.
This is what we mean by keeping rendering pure. Read the inputs and calculate the JSX. Do not change the props, increment a shared variable, edit the DOM, or start a network request while doing that calculation. Otherwise, rendering again could repeat work that nobody asked for. Work caused by a click belongs in the click handler. We will trace one below.
Look at InterestCard again. Which values does it read? Does returning its JSX change either value?
Reusing the component
Add a second card below the first one:
<InterestCard
title="Photography"
description="I like taking pictures around Hoboken."
/>
Save the file. The page should show two cards with different words. Both use the code in InterestCard.jsx.
Change the two cards to describe your interests. Add a third InterestCard if you have another idea. Change the prop values in App.jsx; do not copy the component function.
Here is the complete App.jsx for comparison:
import InterestCard from "./InterestCard.jsx";
export default function App() {
return (
<main>
<h1>Hello, I'm Alex</h1>
<p>I'm a student at Stevens.</p>
<img
className="profile-image"
src="/avatar.svg"
alt="A blue smiling face"
width="120"
height="120"
/>
<section aria-labelledby="interests-heading">
<h2 id="interests-heading">Things I like</h2>
<div className="interest-list">
<InterestCard
title="Video games"
description="I like learning how games are designed."
/>
<InterestCard
title="Photography"
description="I like taking pictures around Hoboken."
/>
</div>
</section>
</main>
);
}
The merch project will use the same idea. A ProductCard component can contain the shared structure, while each use passes a different product's information.
What happens when we click a button?
Let's say our card says "Video games". We want a button that changes it to "Board games". The button needs to do more than change a JavaScript variable. React also needs to know that it should calculate the page again.
We now have two cards. Keep that version of App.jsx so you can restore it after this experiment. For example, copy it into an unsaved editor tab. Replace App.jsx with this complete file:
import { useState } from "react";
import InterestCard from "./InterestCard.jsx";
export default function App() {
const [title, setTitle] = useState("Video games");
function handleClick() {
setTitle("Board games");
}
return (
<main>
<h1>My interests</h1>
<button type="button" onClick={handleClick}>
Show board games
</button>
<section aria-labelledby="demo-heading">
<h2 id="demo-heading">Things I like</h2>
<InterestCard
title={title}
description="I like learning how games are designed."
/>
</section>
</main>
);
}
Keep the InterestCard.jsx we already made. It reads props.title and props.description and returns an article, h3, and p.
useState lets a component keep information between renders. We import it from React because it is a React function. On the first render, useState("Video games") gives us two values. title is the current value, and setTitle is the function we call to request a different value. The square brackets give those two returned values separate names. This syntax is called array destructuring.
React remembers this state while this App stays in the same place in the component tree. On later renders, useState gives us the stored value. Calling App again does not start the title over. Keep useState at the top of the component, outside conditions, loops, and event handlers, so React can match each call with the same stored state. Reloading this page does start it over, because this example does not save state outside the running page.
The click is an event. An event is a notification that something happened, such as a click or a key press. handleClick is the event handler, the function we want React to call for this button's click. onClick={handleClick} passes the function. Adding parentheses, as in onClick={handleClick()}, would call it while calculating the page instead of waiting for the click.
Save App.jsx. The card should say "Video games". Click Show board games. The heading should become "Board games", and the paragraph should keep its original words. You do not need to refresh the browser.
From the click to the screen
Read this diagram from left to right. Follow the title from the button to the updated heading:
Wiki image placement: Upload react-click-update.png and insert it here as a full-width image. Use this description: "A click requests a new title. React calculates the result and updates the heading in the DOM. The browser shows Board games and keeps the paragraph." Then remove this editor note.
Running the component functions to calculate what the page should contain is called rendering in React. Here, React calls App and then InterestCard. InterestCard returns JSX describing a heading and paragraph. Returning JSX does not directly edit the visible page.
React compares that result with the previous result. In this example, the card still has the same element types in the same positions. The heading needs different text. The paragraph needs no change. React applies the required DOM changes during the commit. This use of "commit" has nothing to do with saving files in Git.
The browser then uses the DOM and CSS to draw what you see. Calculating sizes and positions is called layout. Drawing pixels is called painting. A longer heading might wrap onto another line, so a small DOM change can also affect the card's height.
Notice where each value comes from. App owns the state. InterestCard receives the current title as a prop. A prop is an input passed by a parent component. State and props can contain the same text, but they have different jobs. The child reads props.title. It does not assign a new value to that prop.
If we used let title = "Video games" inside App and assigned title = "Board games" in the handler, the assignment would not request another render. Also, that local variable would start as "Video games" whenever App ran again. State supplies both the remembered value and the function that requests an update.
Calling setTitle requests the next render. It does not replace the title value inside the handler that is already running. This is why we describe the flow as a new render with new data.
Try changing the handler to request "Card games". Before you click, predict which text will change. Then use Tab to focus the button and press Enter. A real button gives us that keyboard behavior too.
Check your explanation without reading the code. What starts the update? Which component owns the changing title? Which value reaches the card? What changes in the browser? The answers are the click handler's state update, App, the title prop, and the heading's text.
What is React comparing?
JSX produces descriptions called React elements. For example, <h3>{props.title}</h3> describes a heading and its current text. A React element is a JavaScript value. It is not the live DOM object returned by querySelector.
InterestCard is the component function. <InterestCard title={title} /> describes one use of that function, including its inputs. When React renders the component, it calls the function to get the elements inside the card.
Wiki image placement: Upload react-elements-to-dom.png and insert it here as a full-width image. Use this description: "A component, React elements, and the live DOM." Then remove this editor note.
Here are the three meanings of element in this lesson:
| Place | What we are looking at |
|---|---|
| HTML source | <h3>Video games</h3> is markup describing a heading element. |
| Browser DOM | A live heading object, which querySelector can return. Its text can change. |
| React code | JSX produces a React element describing what React should display. |
This picture separates the component tree from the browser DOM tree:
Image from React's Understanding your UI as a tree, React documentation contributors, CC BY 4.0. Displayed without alteration.
For our two-card page, imagine A is App and B and C are its two uses of InterestCard. Both cards use the same function. Each card returns several elements, so the component tree and the DOM tree have different jobs and different numbers of parts.
People often call React's in-memory description of the interface the virtual DOM. It is not another visible page or a copy of every object in the browser's document. The real DOM is the browser's current document. React uses its descriptions to decide what that document needs next.
React's comparison is called reconciliation. React uses element type and position to match parts across renders. In our example, the article, h3, and p remain in the same positions, so React can keep those elements and change the heading text. If we replaced an h3 with an input, React would need a different DOM element.
Component state also depends on identity. Removing a component, or replacing its type in that position, can discard its state. Next week, list keys will help React match items when a list changes order. We do not need keys for the two fixed card uses we wrote today.
The first render and later renders
On the first visit, main.jsx calls root.render with <App />. React calls App and the components it uses, creates their DOM elements, and adds them inside the root. The browser can then draw the page.
Later, our state setter requests an update. React calls App with the new state and normally renders its children too. A child can render even if its props did not change. React compares the result and commits any required DOM changes. A render can produce the same result and require no DOM change.
Keep the component's calculation pure. Here that means reading the current props and state and returning JSX. Do not change a prop, directly edit the DOM, or send a network request while calculating that JSX. React can call the component more than once. The starter's StrictMode deliberately repeats some development work to help catch mistakes, so a console message inside the component may appear twice. That does not mean the person clicked twice.
Compare these different kinds of update:
| Action | What happens |
|---|---|
Save App.jsx while Vite runs |
The development tool sends the edited code to the browser. |
| Click our button | The handler requests new state, and React calculates the next interface. |
| Resize the window | The browser can change layout using CSS without React calling the component again. |
| Reload the browser | The page starts again. Our temporary title state starts as "Video games". |
Restore the two-card App.jsx from Reusing the component before continuing. The final practice page for today keeps those two fixed cards. This experiment shows how Week 5's changing data will reach the components you made.
How the cards get their styles
The starter still uses ordinary CSS. Open src/styles.css and find .interest-card. The dot means this rule selects a class, so it styles each element whose JSX says className="interest-card". Unlike an ID, the same class can appear on several elements.
Now find the list rule:
.interest-list {
display: grid;
gap: 12px;
}
The div with className="interest-list" groups the cards. display: grid lets CSS arrange its children in rows and columns. This rule uses one column, so every new card gets a row. gap adds space between them.
Try changing gap to 24px. The space between cards should grow while the padding inside each card stays the same. Restore 12px after checking it.
Now let the cards sit side by side when the browser has room. Add this after the closing brace of the existing .interest-list rule:
@media (min-width: 600px) {
.interest-list {
grid-template-columns: 1fr 1fr;
}
}
A media query applies CSS only when its condition is true. Here, min-width: 600px checks whether the browser's page area is at least 600 CSS pixels wide. It checks the browser width, not the width of the card. The rules inside its braces apply at that width and above.
grid-template-columns sets the columns. Each 1fr asks for one equal share of the available space, so 1fr 1fr makes two equal columns. Below 600 pixels, this rule stops applying and the original one-column grid remains. This is a responsive layout: the arrangement changes to fit the available space.
Save, then resize the browser. Check a narrow view around 390 pixels and a wide view around 900 pixels. Use the browser's device toolbar in Inspect if the window will not get narrow enough. You should see one column in the narrow view, two in the wide view, and space between the cards in both. The text should remain readable without horizontal scrolling.
Keep this media query in your page. It is an extra practice change beyond the earlier reference files, which use one column. A merch catalog can use the same technique for its product cards.
A shorter way to read props
This is optional practice after your page works. You may see a component receive props with this syntax:
export default function InterestCard({ title, description }) {
Then remove props. from the two values inside the JSX:
<h3>{title}</h3>
<p>{description}</p>
Save and check the page. It should look the same. We changed how the component receives its props, not what it displays.
This syntax is called object destructuring. React still passes one props object. The braces select its title and description properties and give us variables with those names. function InterestCard(props) followed by props.title and function InterestCard({ title }) followed by title read the same input. There is no new React behavior here.
An unknown prop is still undefined. Destructuring does not repair a misspelled prop name. You can keep the original props.title version if it is easier to follow.
Debugging a prop
Use this short demonstration if time allows, or try it after the meeting.
In the second card in App.jsx, change description to the misspelled name descripton:
<InterestCard
title="Photography"
descripton="I like taking pictures around Hoboken."
/>
Save the file. The second heading remains, but its paragraph becomes blank.
InterestCard asks for a prop named description. The parent passed a different name, so description has no value to display. This mistake might not show a red error screen.
When part of a component is blank, compare the prop names where the component is used and where it receives them. Restore the missing i, save, and check that the paragraph returns.
If the browser shows an error screen, read its filename and line number. JSX errors often come from an unclosed tag, brace, quote, or parenthesis near that line.
Saving the React page with Git
Open a second integrated terminal so the development server can keep running. The new terminal should start at the top of practice-page. If its prompt ends with react-page, run cd .. once before continuing.
First, run the React project's build check:
cd react-page
npm run build
cd ..
The build should finish without an error. A folder named dist will appear, but the project's .gitignore keeps it out of Git.
Run git status, then open Source Control in VS Code. Read the changed React files and use the plus buttons to stage them. Check the selection in the terminal:
git diff --staged --stat
git diff --staged -- react-page/src/App.jsx react-page/src/InterestCard.jsx
Read the staged files. node_modules and dist should not appear. node_modules contains packages downloaded by npm install; another person can restore them from package-lock.json.
If either ignored folder appears, stop and ask for help before committing.
In Source Control, enter Build reusable interest cards with React as the commit message and select Commit. Select Publish Branch to send react-profile to GitHub. If the branch is already published, choose Push from the ... menu instead.
Use the GitHub Pull Requests view in VS Code to create a pull request into main. In the description, say what you built and what you checked. For example:
I rebuilt part of my practice page with an InterestCard component and passed different content through props. I opened the local page and ran
npm run build.
Ask your Week 3 partner to review the component before the next meeting.
Common problems
| What you see | What to check |
|---|---|
node or npm is not found |
Install the current Node.js LTS release, then completely close and reopen VS Code. |
Windows says scripts are disabled when you run npm |
In VS Code, choose Terminal > Select Default Profile > Command Prompt, open a new terminal, and try again. |
npm cannot find package.json |
Run cd react-page before npm run dev or npm run build. |
| The browser is blank or shows the wrong page | Use the exact local address printed by the current npm run dev command. Do not open index.html directly. |
| A card heading appears without its description | Compare the prop names in App.jsx and InterestCard.jsx. |
| The browser shows a JSX error | Start at the named file and line. Match each opening tag, brace, quote, and parenthesis with its closing one. |
| The local address does not use port 5173 | Use the address printed by the development server. It selects another port when 5173 is busy. |
| Git shows thousands of files | Stop before committing. Check that react-page/.gitignore exists and that node_modules and dist are listed in it. |
| The branch name already exists | Run git switch react-profile instead of creating it again. |
| A push asks for a GitHub username or password | Run gh auth status and gh auth setup-git, then try the push again. |
Before next week
Close the example for a moment. Explain why we kept the same HTML elements and CSS when moving to React. Name one benefit and one cost of that choice. Explain why a component rendering again does not necessarily replace its DOM elements.
Open the code again. Show someone the InterestCard function, one place where it is used, and one prop that moves between them. If you changed a CSS class inside InterestCard, predict how many cards would change. Then check your prediction. Resize the browser to show one column below 600 pixels and two columns at 600 pixels or wider.
Make sure your branch is pushed and your pull request is open. Keep the original page and the react-page folder in the same repository.
Next week, we will put the card information into an array and use it to make a list. We will use state again to make a search field respond as someone types. After that, we will practise loading API data, using a cart, and following checkout before starting the shared merch project in Week 9.
The complete App.jsx, InterestCard.jsx, and setup files are on this page. Use them to compare a file that is not working.
Creating the starter
Do this before the meeting if you do not have practice-page/react-page. Skip this section if that starter already runs. Do not replace files you have already edited.
Use desktop VS Code. Install the current LTS release from the Node.js download page, then close and reopen VS Code. Node runs our development tools. npm comes with Node and downloads the packages listed in package.json.
Open Terminal > New Terminal and run:
node --version
npm --version
Both commands should print a version number. This starter's Vite 8 needs a supported Node version, at least Node 20.19 or Node 22.12 on those release lines. The current LTS is the simpler choice for a new installation. If Windows blocks npm as a PowerShell script, select Terminal > Select Default Profile > Command Prompt, open a new terminal, and retry.
Inside practice-page, create a folder called react-page in VS Code. Inside it, create src and public. Make the following files in those folders. Do not run git init inside react-page. It belongs to the repository from Week 3.
practice-page/
index.html your earlier page stays here
react-page/
.gitignore
package.json
index.html
vite.config.js
public/
avatar.svg
src/
main.jsx
App.jsx
styles.css
package.json
This names the commands and packages for the starter. A dependency is code the project needs from another package. npm uses this file when it installs them.
{
"name": "react-page",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.2.8",
"react-dom": "^19.2.8"
},
"devDependencies": {
"@vitejs/plugin-react": "^6.1.0",
"vite": "^8.2.2"
}
}
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My React page</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
});
src/main.jsx
The root ID below matches the div in index.html. createRoot lets React manage the contents of that element. The call to .render starts the first render. Leave this file in place while you edit components.
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.jsx";
import "./styles.css";
createRoot(document.getElementById("root")).render(
<StrictMode>
<App />
</StrictMode>,
);
src/App.jsx
This is the starting page, before we extract the card into its own file. The complete two-card version earlier in the lesson is the result to compare against when you finish.
export default function App() {
return (
<main>
<h1>Hello, I'm Alex</h1>
<p>I'm a student at Stevens.</p>
<img
className="profile-image"
src="/avatar.svg"
alt="A blue smiling face"
width="120"
height="120"
/>
<section aria-labelledby="interests-heading">
<h2 id="interests-heading">Things I like</h2>
<div className="interest-list">
<article className="interest-card">
<h3>Video games</h3>
<p>I like learning how games are designed.</p>
</article>
</div>
</section>
</main>
);
}
src/styles.css
* {
box-sizing: border-box;
}
body {
font-family: system-ui, sans-serif;
background: #eef2ff;
color: #172554;
margin: 0;
padding: 24px;
}
main {
max-width: 680px;
margin: 0 auto;
background: white;
padding: 24px;
border-radius: 16px;
}
h1 {
color: #1d4ed8;
text-align: center;
}
.profile-image {
border-radius: 16px;
}
.interest-list {
display: grid;
gap: 12px;
}
.interest-card {
border: 1px solid #bfdbfe;
border-radius: 12px;
padding: 16px;
}
.interest-card h3,
.interest-card p {
margin: 0;
}
.interest-card p {
margin-top: 8px;
}
button {
font: inherit;
padding: 10px 16px;
border: 0;
border-radius: 8px;
background: #1d4ed8;
color: white;
cursor: pointer;
}
button:focus-visible {
outline: 3px solid #172554;
outline-offset: 3px;
}
public/avatar.svg
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 120 120">
<rect width="120" height="120" rx="16" fill="#dbeafe"/>
<circle cx="60" cy="60" r="43" fill="#2563eb"/>
<circle cx="45" cy="49" r="5" fill="white"/>
<circle cx="75" cy="49" r="5" fill="white"/>
<path d="M40 70 Q60 90 80 70" fill="none" stroke="white" stroke-width="6" stroke-linecap="round"/>
</svg>
.gitignore
These folders can be generated again, so Git should not include them.
node_modules
dist
*.local
In a terminal at the top of practice-page, run:
cd react-page
npm install
npm run dev
npm install downloads the packages into node_modules and records their resolved versions in package-lock.json. Keep package-lock.json in Git so others can install the same versions. The development command prints the local address to open. You should see the profile, blue avatar, and one Video games card.
To stop the server, press Ctrl+C in that terminal. Then return to Starting the React page and create your lesson branch. The page is now ready for the component exercise.
Resources
Everything needed for today's code and explanations is above. These are extra examples or references to return to after the exercise.
- MDN: Document Object Model explains the browser objects behind the page.
- React: Your first component gives more component examples.
- React: Writing markup with JSX covers JSX syntax.
- React: Passing props to a component explains inputs and destructuring.
- React: Responding to events gives more handler examples.
- React: State, a component's memory explains remembered values and setters.
- React: Render and commit gives another page-update trace.
- React: Preserving and resetting state explains component identity.
- MDN: CSS Grid gives more layout examples.
- MDN: Using media queries explains responsive conditions.
- Vite: Getting started documents the development and build tools.
Next week, we will use arrays, lists, and a search field. If you cannot explain a step in your own words, ask a Tech Team lead or bring that step to the next meeting.