React Basics #4: Components and Props
Last time we looked at JSX syntax. Along the way we naturally saw a function named App. That App is actually React’s most important unit — a component. In this post we’ll cover what a component is and how to build one, plus props, the channel for passing data between components.
Why do we need components? #
Imagine writing the entire screen as one giant function. Header, sidebar, main content, footer, buttons, input fields… your code quickly hits hundreds or thousands of lines, and finding what to fix becomes nearly impossible. If you have ten buttons of the same shape, you’d have to write the same code ten times.
React solves this with the concept of a component. A component is a reusable unit that represents a piece of the screen. Once you’ve defined screen elements like headers, buttons, cards, and input fields each as their own component, you can use them anywhere just like an HTML tag.
Creating your first component #
In React, a component is ultimately a JavaScript function that returns JSX. The App we’ve been seeing is one too.
function Greeting() {
return <h1>Hello, React!</h1>;
}
function App() {
return (
<div>
<Greeting />
</div>
);
}
export default App;We define a new function called Greeting and use it inside App like an HTML tag: <Greeting />. A single function becomes a single component, just like that.
<greeting /> makes React treat it as a regular HTML tag, leading to unintended behavior. The naming convention: PascalCase (UserCard, LoginButton) is standard.Splitting components into separate files #
As the number of components grows, splitting them into separate files is better than cramming everything into one. The usual practice is one component per file.
Create a new file src/Greeting.jsx:
function Greeting() {
return <h1>Hello, React!</h1>;
}
export default Greeting;Then in App.jsx, import it:
import Greeting from './Greeting';
function App() {
return (
<div>
<Greeting />
</div>
);
}
export default App;export default for export and import for import — the standard JavaScript module pattern. You can omit the file extension (.jsx); Vite will find it.
Greeting.jsx or UserCard.jsx. The folder structure varies by project, but small projects commonly group components under src/components/.Passing data to a component — props #
Our Greeting component always prints “Hello, React!”. But what if you want to show a different greeting per user? props is the answer.
Props are like a component’s parameters — the way a parent passes data down to a child. You write them just like HTML attributes.
import Greeting from './Greeting';
function App() {
return (
<div>
<Greeting name="Cheolsu" />
<Greeting name="Younghee" />
<Greeting name="Minsu" />
</div>
);
}
export default App;We use Greeting three times, passing a different name value each time. Now let’s update Greeting to receive that value.
function Greeting(props) {
return <h1>Hello, {props.name}!</h1>;
}
export default Greeting;The function’s first parameter is an object called props. Every attribute the parent passed in becomes a property of that object. Since we passed name="Cheolsu", props.name is 'Cheolsu'.
The screen prints:
Hello, Cheolsu!
Hello, Younghee!
Hello, Minsu!The same component is reused three times with only the props changing. That’s the core value of components.
Various types of props #
You can pass not only strings but numbers, booleans, arrays, objects, even functions as props. Just remember that non-string values must be wrapped in { }.
function App() {
const user = { name: 'Cheolsu', email: 'cheolsu@example.com' };
return (
<UserCard
name="Cheolsu"
age={30}
isAdmin={true}
hobbies={['reading', 'coding', 'travel']}
profile={user}
/>
);
}function UserCard(props) {
return (
<div>
<h2>{props.name} ({props.age})</h2>
{props.isAdmin && <p>Has admin privileges.</p>}
<p>Email: {props.profile.email}</p>
<p>Hobbies: {props.hobbies.join(', ')}</p>
</div>
);
}
export default UserCard;Strings go in quotes (name="Cheolsu"), and any other JavaScript value goes in curly braces (age={30}).
Receiving cleanly with destructuring #
Typing props.name, props.age, and so on gets tedious. Use JavaScript’s destructuring assignment:
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}
export default Greeting;By destructuring at the parameter slot, the function body just uses name directly, and the code shortens. For multiple props, list them all:
function UserCard({ name, age, isAdmin, hobbies, profile }) {
return (
<div>
<h2>{name} ({age})</h2>
{isAdmin && <p>Has admin privileges.</p>}
<p>Email: {profile.email}</p>
<p>Hobbies: {hobbies.join(', ')}</p>
</div>
);
}
export default UserCard;This style is far more common in real React code, and we’ll use it from now on.
Default values #
When a prop might not be provided, you can specify a default with destructuring:
function Greeting({ name = 'Guest' }) {
return <h1>Hello, {name}!</h1>;
}Calling <Greeting /> without name automatically uses 'Guest'.
children — content between component tags #
So far we’ve used components in self-closing form like <Greeting />. But sometimes, like in HTML, you want to put something between an opening and a closing tag.
import Card from './Card';
function App() {
return (
<Card>
<h2>Notice</h2>
<p>We're closed today.</p>
</Card>
);
}In this case, the content between <Card> and </Card> is automatically passed as a special prop called children.
function Card({ children }) {
return (
<div className="card" style={{ border: '1px solid #ccc', padding: '16px', borderRadius: '8px' }}>
{children}
</div>
);
}
export default Card;Card has no idea what content will go inside, but all it does is render children in that place. This pattern shows up constantly in layout components (Card, Modal, Layout) and wrapper components.
Props are read-only #
Last but certainly not least: a component must never modify the props it receives. The code below is incorrect.
function Greeting({ name }) {
name = name.toUpperCase(); // 🚫 directly mutating props
return <h1>Hello, {name}!</h1>;
}Props are a copy of data flowing down from the parent — not a value the child gets to mutate at will. If you need to transform something, store it in a new variable.
function Greeting({ name }) {
const upperName = name.toUpperCase();
return <h1>Hello, {upperName}!</h1>;
}Try it yourself #
Restructure the src/App.jsx from last time as follows.
Create src/UserCard.jsx:
function UserCard({ name, age, hobbies }) {
return (
<div style={{ border: '1px solid #ccc', padding: '12px', margin: '8px', borderRadius: '8px' }}>
<h2>{name} ({age})</h2>
<p>Hobbies: {hobbies.join(', ')}</p>
</div>
);
}
export default UserCard;And src/App.jsx:
import UserCard from './UserCard';
function App() {
return (
<>
<h1>Member List</h1>
<UserCard name="Cheolsu" age={30} hobbies={['reading', 'coding']} />
<UserCard name="Younghee" age={28} hobbies={['travel', 'cooking', 'photography']} />
<UserCard name="Minsu" age={35} hobbies={['gaming']} />
</>
);
}
export default App;Save and three member cards appear on screen. The same UserCard component is reused three times with different props. Try changing the names or hobbies, or adding a new card.
Wrapping up #
In this post we built components — React’s core unit — split them into separate files, and learned to pass data using props. We also covered destructuring, default values, children, and the read-only rule. You can now divide a screen into small reusable pieces.
So far every component we’ve built has been static. Once rendered, they never changed. But real apps constantly change in response to user input, time, and server responses. In the next post, “React Basics #5: State and useState,” we’ll learn how a component handles changeable data — the concept of state and the useState hook.