The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Build a small searchable product list to see how React components, JSX, props, events, and state work together. This tutorial assumes you know basic JavaScript and web markup. It uses Vite as a learning-focused setup—not as a claim that a bare build tool is the right foundation for every production app.
Choose a setup for this learning project
React’s documentation recommends starting a new app with a framework: “If you want to build a new app or website with React, we recommend starting with a framework.” (React documentation: Start a New React Project.) A framework is the more suitable starting point when you already need app features such as routing or data loading.
As an Amazon Associate I earn from qualifying purchases.
For this deliberately small exercise, Vite keeps the setup focused on client-side React. That is a learning choice, not a universal production recommendation: as the app grows, you will need to choose features such as routing and data fetching. React’s documentation describes building from scratch as a reasonable option when constraints or learning goals call for it, while warning that you will own those decisions. A framework does not necessarily mean server-only rendering; supported frameworks can also deploy client-rendered or static apps.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errors| Path | Useful when | What you take on |
|---|---|---|
| Framework starter | You want React’s recommended starting point for a new app and solutions for common app needs. | Learn the framework’s conventions and features. |
| Vite build-tool starter | You want a compact project for learning client-side React. | Choose and add app features such as routing and data fetching as needs arise. |
React’s installation documentation says, “No. Create React App has been deprecated.” (React documentation: Installation.) Do not use Create React App for a new tutorial project. If you want to try React without configuring a local project, React’s installation page also points to CodeSandbox, StackBlitz, and CodePen.
#1 Best Overall
Create a Vite project
With Node.js installed, create a React project and start its development server:
npm create vite@latest react-product-list -- --template react
cd react-product-list
npm install
npm run dev
Open the local address printed in the terminal. Vite is a build tool with a development server and a production build command; it offers React and React TypeScript templates. The example here uses the JavaScript template to keep the first project small. If you choose the TypeScript template instead, the files and code will need TypeScript syntax.
Plan the interface before adding behavior
The app has a heading, a search box, an “In stock only” checkbox, and a list of products. First identify the pieces that can be represented as components:
- App: owns the page and the data shared between controls and results.
- SearchBar: displays the search input and checkbox.
- ProductList: displays the matching products.
- ProductRow: displays one product’s name, category, price, and availability.
Start with structure and sample data. React’s “Thinking in React” guidance uses a searchable product table and recommends identifying the component hierarchy before wiring up interaction. Keeping the first pass static makes it easier to see how the pieces fit.
Render products with components, JSX, and props
A React component is a JavaScript function that returns UI markup. JSX lets you write markup alongside the JavaScript that determines what appears. Component names start with a capital letter, and JSX tags must be closed. If a component returns several sibling elements, wrap them in one parent element or a fragment. In JSX, use className for CSS classes, and use curly braces to insert JavaScript values or expressions.
Replace the contents of src/App.jsx with the following. It creates the data once, passes products through props, and renders a row for each product:
Rank #3
const products = [
{ id: 1, name: "Canvas Backpack", category: "Bags", price: 48, inStock: true },
{ id: 2, name: "Ceramic Travel Mug", category: "Kitchen", price: 22, inStock: false },
{ id: 3, name: "Desk Lamp", category: "Home", price: 36, inStock: true },
{ id: 4, name: "Wool Scarf", category: "Clothing", price: 30, inStock: true },
];
function ProductRow({ product }) {
return (
<li className="product-row">
<div>
<h3>{product.name}</h3>
<p>{product.category}</p>
</div>
<p>${product.price.toFixed(2)}</p>
<p>{product.inStock ? "In stock" : "Out of stock"}</p>
</li>
);
}
function SearchBar({ query, onQueryChange, inStockOnly, onStockChange }) {
return (
<form className="controls" onSubmit={(event) => event.preventDefault()}>
<label>
Search products
<input
type="search"
value={query}
onChange={(event) => onQueryChange(event.target.value)}
placeholder="Try “lamp”"
/>
</label>
<label className="checkbox-label">
<input
type="checkbox"
checked={inStockOnly}
onChange={(event) => onStockChange(event.target.checked)}
/>
In stock only
</label>
</form>
);
}
function ProductList({ products }) {
if (products.length === 0) {
return <p>No products match those filters.</p>;
}
return (
<ul className="product-list">
{products.map((product) => (
<ProductRow key={product.id} product={product} />
))}
</ul>
);
}
export default function App() {
return (
<main className="app">
<h1>Product list</h1>
<SearchBar />
<ProductList products={products} />
</main>
);
}
The markup is not decorative text: JSX expressions such as {product.name} insert values into the rendered interface. The map call turns the data array into rows. Each row receives a stable key based on its product ID so React can track list items as the list changes.
Props are values passed from a parent component to a child. Here, ProductList receives a products prop, then passes one product to each ProductRow. The initial SearchBar has no behavior yet; the next step connects its inputs to state.
Add state and connect the controls
State is a component’s memory for values that change over time. The search text and checkbox selection can change when someone interacts with the page, so they belong in state. Both the controls and results depend on them; keep that shared state in their closest common parent, App, and pass values and event handlers down as props.
Rank #4
Import useState at the top of src/App.jsx:
import { useState } from "react";
Then replace the App function from the previous example with this version:
export default function App() {
const [query, setQuery] = useState("");
const [inStockOnly, setInStockOnly] = useState(false);
const visibleProducts = products.filter((product) => {
const matchesQuery = product.name
.toLowerCase()
.includes(query.trim().toLowerCase());
const matchesStock = !inStockOnly || product.inStock;
return matchesQuery && matchesStock;
});
return (
<main className="app">
<h1>Product list</h1>
<SearchBar
query={query}
onQueryChange={setQuery}
inStockOnly={inStockOnly}
onStockChange={setInStockOnly}
/>
<ProductList products={visibleProducts} />
</main>
);
}
Each input is controlled: its displayed value comes from state, and its onChange handler requests a state update. Typing updates query; changing the checkbox updates inStockOnly. React then renders again with the new values, recalculates visibleProducts, and gives the matching list to ProductList.
Recommended Free Tools
This is one-way data flow: the parent owns the shared values and passes them down, while children call handlers provided by the parent to request updates. The visible list is calculated from the products and current filters, so it does not need a separate state variable to stay synchronized.
Best Value
Style the result and check the behavior
Add a basic stylesheet in src/index.css so the structure is easy to inspect:
:root {
font-family: system-ui, sans-serif;
color: #202124;
background: #f5f6f8;
}
body {
margin: 0;
}
.app {
max-width: 680px;
margin: 3rem auto;
padding: 1.5rem;
background: white;
border-radius: 12px;
}
.controls {
display: grid;
gap: 1rem;
margin: 1.5rem 0;
}
.controls label {
display: grid;
gap: 0.4rem;
}
.controls .checkbox-label {
display: flex;
align-items: center;
gap: 0.5rem;
}
input[type="search"] {
padding: 0.7rem;
font: inherit;
}
.product-list {
padding: 0;
list-style: none;
}
.product-row {
display: grid;
grid-template-columns: 1fr auto auto;
gap: 1rem;
align-items: center;
padding: 1rem 0;
border-top: 1px solid #ddd;
}
.product-row h3,
.product-row p {
margin: 0;
}
.product-row div p {
margin-top: 0.3rem;
color: #60656b;
}
Try searching for “lamp,” then enable “In stock only.” Clear the search and toggle the checkbox to see each control affect the results. Searching is case-insensitive and trims spaces at either end; a non-matching search displays the empty-state message.
What to change when the app grows
The sample catalogue is static data in the file, which is useful for learning rendering and state without adding a backend. If you later let users add, edit, or remove products, update arrays and objects in state by creating new values rather than mutating existing ones. For example, use setProducts(current => current.map(...)) to produce an updated array instead of changing an item in place.
A small Vite project gives you a development server and build command, but it does not supply production routing, data fetching, or server rendering as an app architecture. When those requirements matter, revisit React’s guidance on starting a new React project and choose a framework or deliberately add the capabilities your application needs. Vite’s official guide documents its React templates and workflow at Vite Guide.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




