React Fundamentals
React is a JavaScript library for building user interfaces. This guide will take you from zero to building your first React applications.
Why React?
The Problem: Traditional JavaScript requires manual DOM manipulation, making complex UIs difficult to maintain.
The Solution: React uses a component-based approach where you describe what the UI should look like, and React handles the updates efficiently.
Benefits:
Your First React App
Setup
The easiest way to start is with Vite:
# Create new React project
npm create vite@latest my-first-app -- --template react
# Navigate to project
cd my-first-app
# Install dependencies
npm install
# Start development server
npm run dev
Open your browser to http://localhost:5173
Hello World
Replace src/App.jsx with:
function App() {
return <h1>Hello, React!</h1>;
}
export default App;
Congratulations! You just created your first React component.
Understanding JSX
JSX looks like HTML but it's actually JavaScript.
// This JSX...
const element = <h1>Hello!</h1>;
// ...gets transformed to...
const element = React.createElement('h1', null, 'Hello!');
JSX Basics
// Text content
<h1>Hello World</h1>
// Using JavaScript expressions
const name = "Sarah";
<h1>Hello {name}!</h1>
// Attributes
<img src="photo.jpg" alt="My photo" />
// Children
<div>
<h1>Title</h1>
<p>Paragraph</p>
</div>
// Fragments (no extra DOM element)
<>
<h1>Title</h1>
<p>Paragraph</p>
</>
Important Rules
// ❌ Wrong: class (JavaScript keyword)
<div class="container">
// ✅ Correct: className
<div className="container">
// ❌ Wrong: Multiple root elements
return (
<h1>Title</h1>
<p>Text</p>
)
// ✅ Correct: Single root or Fragment
return (
<>
<h1>Title</h1>
<p>Text</p>
</>
)
// ❌ Wrong: Unclosed tag
<img src="photo.jpg">
// ✅ Correct: Self-closing
<img src="photo.jpg" />
Components
Components are the building blocks of React apps.
Function Components
// Simple component
function Welcome() {
return <h1>Welcome to my app!</h1>;
}
// Arrow function component
const Welcome = () => {
return <h1>Welcome to my app!</h1>;
};
// Using the component
function App() {
return (
<div>
<Welcome />
<Welcome />
<Welcome />
</div>
);
}
Component with Logic
function Greeting() {
const hour = new Date().getHours();
const timeOfDay = hour < 12 ? "morning" : hour < 18 ? "afternoon" : "evening";
return <h1>Good {timeOfDay}!</h1>;
}
Props - Passing Data
Props let you pass data from parent to child components.
// Parent component
function App() {
return (
<div>
<UserCard name="Alice" age={25} />
<UserCard name="Bob" age={30} />
<UserCard name="Charlie" age={35} />
</div>
);
}
// Child component
function UserCard(props) {
return (
<div className="card">
<h2>{props.name}</h2>
<p>Age: {props.age}</p>
</div>
);
}
// Destructured props (cleaner)
function UserCard({ name, age }) {
return (
<div className="card">
<h2>{name}</h2>
<p>Age: {age}</p>
</div>
);
}
Props Are Read-Only
function UserCard({ name }) {
// ❌ Never modify props
name = "Modified"; // This is wrong!
// ✅ Props should be treated as read-only
return <h2>{name}</h2>;
}
Children Prop
function Card({ children, title }) {
return (
<div className="card">
<h3>{title}</h3>
<div>{children}</div>
</div>
);
}
// Usage
<Card title="My Card">
<p>This is the content</p>
<button>Click me</button>
</Card>
State - Interactive Components
State lets components remember information and change over time.
import { useState } from 'react';
function Counter() {
// Declare state variable
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
How useState Works
const [value, setValue] = useState(initialValue);
// ↑ ↑ ↑
// current updater initial value
// value function
Multiple State Variables
function Form() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(0);
return (
<div>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<input
type="number"
value={age}
onChange={(e) => setAge(e.target.value)}
placeholder="Age"
/>
</div>
);
}
State with Objects
function UserForm() {
const [user, setUser] = useState({
name: '',
email: '',
age: 0
});
const updateField = (field, value) => {
setUser({
...user, // Keep other fields
[field]: value // Update this field
});
};
return (
<div>
<input
value={user.name}
onChange={(e) => updateField('name', e.target.value)}
/>
<input
value={user.email}
onChange={(e) => updateField('email', e.target.value)}
/>
</div>
);
}
Handling Events
React events work similarly to DOM events but with camelCase naming.
function Button() {
const handleClick = () => {
alert('Button clicked!');
};
return <button onClick={handleClick}>Click me</button>;
}
// Inline function
<button onClick={() => alert('Clicked!')}>Click me</button>
// With event parameter
function Form() {
const handleSubmit = (e) => {
e.preventDefault(); // Prevent page reload
console.log('Form submitted');
};
return <form onSubmit={handleSubmit}>...</form>;
}
// Common events
<button onClick={() => {}}>Click</button>
<input onChange={(e) => console.log(e.target.value)} />
<form onSubmit={(e) => e.preventDefault()}>...</form>
<div onMouseEnter={() => {}} onMouseLeave={() => {}}>Hover me</div>
Conditional Rendering
Show different UI based on conditions.
// Using if statement
function Greeting({ isLoggedIn }) {
if (isLoggedIn) {
return <h1>Welcome back!</h1>;
}
return <h1>Please sign in</h1>;
}
// Using ternary operator
function Greeting({ isLoggedIn }) {
return (
<div>
{isLoggedIn ? (
<h1>Welcome back!</h1>
) : (
<h1>Please sign in</h1>
)}
</div>
);
}
// Using && for conditional content
function Notifications({ count }) {
return (
<div>
<h1>Inbox</h1>
{count > 0 && (
<p>You have {count} unread messages</p>
)}
</div>
);
}
Lists and Keys
Render multiple items from an array.
function TodoList() {
const todos = [
{ id: 1, text: 'Learn React' },
{ id: 2, text: 'Build an app' },
{ id: 3, text: 'Deploy it' }
];
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
Why Keys Matter: Keys help React identify which items have changed, been added, or removed.
// ✅ Good: Stable ID
{items.map(item => <li key={item.id}>{item.name}</li>)}
// ⚠️ Only if no stable ID and list never reorders
{items.map((item, index) => <li key={index}>{item}</li>)}
// ❌ Never use random values
{items.map(item => <li key={Math.random()}>{item}</li>)}
Building Real Apps
Example 1: Counter with Reset
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div style={{ textAlign: 'center', marginTop: '50px' }}>
<h1>Counter: {count}</h1>
<div style={{ display: 'flex', gap: '10px', justifyContent: 'center' }}>
<button onClick={() => setCount(count - 1)}>-</button>
<button onClick={() => setCount(0)}>Reset</button>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
</div>
);
}
export default Counter;
Example 2: Todo App
import { useState } from 'react';
function TodoApp() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
const addTodo = () => {
if (input.trim()) {
setTodos([...todos, { id: Date.now(), text: input, done: false }]);
setInput('');
}
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
));
};
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<div style={{ maxWidth: '500px', margin: '50px auto' }}>
<h1>My Todo List</h1>
<div style={{ display: 'flex', gap: '10px', marginBottom: '20px' }}>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && addTodo()}
placeholder="What needs to be done?"
style={{ flex: 1, padding: '10px' }}
/>
<button onClick={addTodo} style={{ padding: '10px 20px' }}>
Add
</button>
</div>
<ul style={{ listStyle: 'none', padding: 0 }}>
{todos.map(todo => (
<li
key={todo.id}
style={{
display: 'flex',
alignItems: 'center',
gap: '10px',
padding: '10px',
background: '#f5f5f5',
marginBottom: '10px',
borderRadius: '5px'
}}
>
<input
type="checkbox"
checked={todo.done}
onChange={() => toggleTodo(todo.id)}
/>
<span
style={{
flex: 1,
textDecoration: todo.done ? 'line-through' : 'none'
}}
>
{todo.text}
</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
{todos.length === 0 && (
<p style={{ textAlign: 'center', color: '#999' }}>
No todos yet. Add one above!
</p>
)}
</div>
);
}
export default TodoApp;
Example 3: Temperature Converter
import { useState } from 'react';
function TemperatureConverter() {
const [celsius, setCelsius] = useState('');
const [fahrenheit, setFahrenheit] = useState('');
const handleCelsiusChange = (value) => {
setCelsius(value);
if (value === '') {
setFahrenheit('');
} else {
const f = (parseFloat(value) * 9/5) + 32;
setFahrenheit(f.toFixed(2));
}
};
const handleFahrenheitChange = (value) => {
setFahrenheit(value);
if (value === '') {
setCelsius('');
} else {
const c = (parseFloat(value) - 32) * 5/9;
setCelsius(c.toFixed(2));
}
};
return (
<div style={{ maxWidth: '400px', margin: '50px auto', textAlign: 'center' }}>
<h1>Temperature Converter</h1>
<div style={{ marginBottom: '20px' }}>
<label style={{ display: 'block', marginBottom: '5px' }}>
Celsius
</label>
<input
type="number"
value={celsius}
onChange={(e) => handleCelsiusChange(e.target.value)}
style={{ padding: '10px', width: '200px' }}
/>
</div>
<div>
<label style={{ display: 'block', marginBottom: '5px' }}>
Fahrenheit
</label>
<input
type="number"
value={fahrenheit}
onChange={(e) => handleFahrenheitChange(e.target.value)}
style={{ padding: '10px', width: '200px' }}
/>
</div>
</div>
);
}
export default TemperatureConverter;
Example 4: Simple Form
import { useState } from 'react';
function ContactForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
message: ''
});
const [submitted, setSubmitted] = useState(false);
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value
});
};
const handleSubmit = (e) => {
e.preventDefault();
console.log('Form data:', formData);
setSubmitted(true);
// Reset form after 3 seconds
setTimeout(() => {
setFormData({ name: '', email: '', message: '' });
setSubmitted(false);
}, 3000);
};
if (submitted) {
return (
<div style={{ maxWidth: '500px', margin: '50px auto', textAlign: 'center' }}>
<h2>✓ Thank you!</h2>
<p>Your message has been sent.</p>
</div>
);
}
return (
<div style={{ maxWidth: '500px', margin: '50px auto' }}>
<h1>Contact Us</h1>
<form onSubmit={handleSubmit}>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px' }}>
Name
</label>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
required
style={{ width: '100%', padding: '10px' }}
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px' }}>
Email
</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
required
style={{ width: '100%', padding: '10px' }}
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px' }}>
Message
</label>
<textarea
name="message"
value={formData.message}
onChange={handleChange}
required
rows={5}
style={{ width: '100%', padding: '10px' }}
/>
</div>
<button
type="submit"
style={{
padding: '10px 30px',
background: '#007bff',
color: 'white',
border: 'none',
borderRadius: '5px',
cursor: 'pointer'
}}
>
Send Message
</button>
</form>
</div>
);
}
export default ContactForm;
Component Organization
As your app grows, organize components into separate files:
src/
├── components/
│ ├── Header.jsx
│ ├── Footer.jsx
│ ├── TodoItem.jsx
│ └── TodoList.jsx
├── App.jsx
└── main.jsx
TodoItem.jsx:
function TodoItem({ todo, onToggle, onDelete }) {
return (
<li>
<input
type="checkbox"
checked={todo.done}
onChange={() => onToggle(todo.id)}
/>
<span style={{ textDecoration: todo.done ? 'line-through' : 'none' }}>
{todo.text}
</span>
<button onClick={() => onDelete(todo.id)}>Delete</button>
</li>
);
}
export default TodoItem;
TodoList.jsx:
import TodoItem from './TodoItem';
function TodoList({ todos, onToggle, onDelete }) {
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={onToggle}
onDelete={onDelete}
/>
))}
</ul>
);
}
export default TodoList;
Styling Your App
Inline Styles
<div style={{ color: 'blue', fontSize: '20px' }}>
Styled text
</div>
CSS Files
Button.css:.btn {
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
}
.btn-primary {
background: #007bff;
color: white;
}
Button.jsx:
import './Button.css';
function Button({ children, variant = 'primary' }) {
return (
<button className={`btn btn-${variant}`}>
{children}
</button>
);
}
Common Mistakes
1. Forgetting to Return
// ❌ Wrong
function MyComponent() {
<div>Hello</div>
}
// ✅ Correct
function MyComponent() {
return <div>Hello</div>;
}
2. Not Using Key in Lists
// ❌ Wrong
{items.map(item => <li>{item}</li>)}
// ✅ Correct
{items.map(item => <li key={item.id}>{item}</li>)}
3. Modifying State Directly
// ❌ Wrong
count = count + 1;
// ✅ Correct
setCount(count + 1);
4. Not Handling Events Correctly
// ❌ Wrong (calls function immediately)
<button onClick={handleClick()}>
// ✅ Correct
<button onClick={handleClick}>
<button onClick={() => handleClick()}>
Next Steps
Now that you understand React fundamentals:
Quick Reference
Essential Hooks
// State
const [value, setValue] = useState(initialValue);
// Side effects (coming in next lessons)
useEffect(() => {
// Run side effects
}, [dependencies]);
Common Patterns
// Mapping arrays
{items.map(item => <Item key={item.id} {...item} />)}
// Conditional rendering
{condition && <Component />}
{condition ? <ComponentA /> : <ComponentB />}
// Handling forms
<input value={value} onChange={(e) => setValue(e.target.value)} />
// Event handling
<button onClick={() => doSomething()}>Click</button>
Resources
Official Docs:
Practice:
Community:
Key Takeaways
Ready to build something amazing? Start with a simple project and gradually add features. The best way to learn React is by building!