React has become a staple in modern web development, powering user interfaces at companies like Facebook, Netflix, and Airbnb. Yet, for beginners, jumping into React can feel like trying to learn a new language while juggling.
This guide is designed to answer three key questions before we start coding:
- Why React?
- How does it work?
- What can you build with it?
By the end, you'll not only understand React's core principles but also create a small working app a Counter to cement your knowledge.
Why ReactJs?
Before React, front-end development largely followed an imperative programming style—telling the browser step-by-step how to update the UI.
The Imperative Way (Pre-React)
document.addEventListener('DOMContentLoaded', function() {
const p = document.createElement('p');
p.textContent = 'This paragraph was added imperatively with JavaScript.';
document.getElementById('content').appendChild(p);
});
This works fine for small tasks but becomes painful when managing:
- Multiple UI elements
- Frequent updates
- Complex state changes
The Declarative Way (React's Approach)
React embraces declarative programming—you describe what the UI should look like, and React figures out how to update it efficiently.
import React from 'react'
function App() {
return (
<div id="content"> <p>This paragraph was added declaratively with React.</p> </div>
);}
export default App;
With React:
- You declare the desired state of your UI
- React automatically updates it when data changes
- You focus on logic, not DOM manipulation
Setting Up Your Environment
Prerequisites
Before diving in, make sure you know:
- HTML tags & structure
- CSS styling basics
- JavaScript fundamentals (variables, functions, arrays, objects)
Install Node.js and npm
1. Download Node.js from nodejs.org
2. Verify installation:
node -v
npm -v
Create Your First React App
We'll use Vite for a fast development setup:
npm create vite@latest my-react-app
cd my-react-app
npm install
npm run dev
Understanding the Project Structure
A well-organized React project follows specific patterns:
src/
├── components/
│ ├── Button.jsx
│ └── Header.jsx
├── hooks/
│ └── useCounter.js
├── pages/
│ └── Home.jsx
├── assets/
├── styles/
└── App.jsx
Key folders:
- components/: Reusable UI components
- hooks/: Custom React hooks
- pages/: Top-level page components
- assets/: Images, fonts, etc.
- styles/: CSS files
JSX — Writing HTML in JavaScript
JSX (JavaScript XML) lets you write HTML-like syntax directly inside JS.
Rules to Remember:
- One parent/root element per return
- camelCase for attributes (className instead of class)
- Every tag must be closed (<img /> not <img>)
- Use curly braces for JavaScript expressions
Example:
function Greeting({ name }) {
return (
<div className="greeting-container">
<h1>Hello, {name}!</h1>
<p>Today is {new Date().toLocaleDateString()}</p>
</div>
)}
Components: The Building Blocks
Components are the heart of React. They're like custom HTML elements that encapsulate logic and UI.
Function Components (Recommended)
// Simple component
function Welcome() {
return <h1>Welcome to React!</h1>;
}
// Component with props
function UserCard({ name, email, role }) {
return (
<div className="user-card">
<h3>{name}</h3>
<p>{email}</p>
<span className="role">{role}</span>
</div>
);
}
// Using the component
function App() {
return (
<div>
<Welcome />
<UserCard name="John Doe" email="john@example.com" role="Developer" />
</div>
);}
Component Best Practices
- Keep components small and focused — One responsibility per component
- Use meaningful names — UserProfile not Component1
- Extract reusable logic — Create custom hooks
- Destructure props — Improves readability
// Good: Destructured props
function Button({ text, onClick, variant = 'primary' }) {
return (
<button className={`btn btn-${variant}`} onClick={onClick} > {text} </button>
);
}
// Usage
<Button text="Click me" onClick={handleClick} variant="secondary" />
State: Making Components Dynamic
State is data that can change over time and belongs to a component. When state changes, React automatically re-renders the component.
The useState Hook
import React, { useState } from 'react';
function Counter() {
// Declare state variable
const [count, setCount] = useState(0);
// Event handlers
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
const reset = () => setCount(0);
return (
<div>
<h2>Count: {count}</h2>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
</div>
);
}
State vs Props

Complex State Example
function TodoApp() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Learn React', completed: false },
{ id: 2, text: 'Build an app', completed: false }
]);
const [inputText, setInputText] = useState('');
const addTodo = () => {
if (inputText.trim()) {
setTodos([
...todos,
{ id: Date.now(), text: inputText, completed: false }
]);
setInputText('');
}};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo ));
};
return (
<div>
<input value={inputText}
onChange={(e) => setInputText(e.target.value)}
placeholder="Add a todo..." />
<button onClick={addTodo}>Add</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }} onClick={() => toggleTodo(todo.id)} >
{todo.text}
</span>
</li> ))}
</ul>
</div>
);
}
Event Handling
React handles events using SyntheticEvents, which provide consistent behavior across browsers.
Common Event Handlers
function EventDemo() {
const [message, setMessage] = useState('');
const handleClick = () => { alert('Button clicked!'); };
const handleSubmit = (e) => {
e.preventDefault(); // Prevent form submission
console.log('Form submitted with:', message); };
const handleInputChange = (e) => {
setMessage(e.target.value);
};
const handleKeyDown = (e) => {
if (e.key === 'Enter') {
console.log('Enter pressed!');
} };
return (
<form onSubmit={handleSubmit}>
<input type="text"
value={message} onChange={handleInputChange}
onKeyDown={handleKeyDown}
placeholder="Type something..." />
<button type="submit">Submit</button>
<button type="button" onClick={handleClick}> Click Me</button>
</form>
); }
Passing Arguments to Event Handlers
function ButtonList() {
const handleClick = (buttonName) => {
alert(`${buttonName} was clicked!`);
};
return (
<div>
<button onClick={() => handleClick('Home')}>Home</button>
<button onClick={() => handleClick('About')}>About</button>
<button onClick={() => handleClick('Contact')}>Contact</button>
</div>
);
}
Conditional Rendering
Conditional Rendering
Methods for Conditional Rendering
1.If/Else Statements
function Greeting({ isLoggedIn, username }) {
if (isLoggedIn) {
return <h1>Welcome back, {username}!</h1>;
} else {
return <h1>Please sign in.</h1>;
}
}
2.Ternary Operator
function StatusMessage({ isOnline }) {
return (
<div>
<span>Status: {isOnline ? 'Online' : 'Offline'}</span>
</div>
);
}
3. Logical && Operator
function Notifications({ messages }) {
return (
<div>
{messages.length > 0 && <div>You have {messages.length} new messages!</div> }
</div>
);
}
4. Switch Statement
function UserRoleBadge({ role }) {
const renderBadge = () => {
switch (role) {
case 'admin':
return <span className="badge badge-red">Admin</span>;
case 'moderator':
return <span className="badge badge-blue">Moderator</span>;
case 'user':
return <span className="badge badge-green">User</span>;
default:
return <span className="badge badge-gray">Guest</span>;
}
};
return <div>{renderBadge()}</div>;
}
Lists and Keys
When rendering lists of elements, React requires each item to have a unique key prop.
Why Keys Are Important
- Help React identify which items have changed
- Optimize rendering performance
- Prevent component state issues during re-renders
Basic List Rendering
function ShoppingList({ items }) {
return (
<ul>
{items.map(item => ( <li key={item.id}> {item.name} - ${item.price} </li> ))}
</ul>
); }
// Usage
const groceries = [
{ id: 1, name: 'Apples', price: 2.99 },
{ id: 2, name: 'Bread', price: 1.99 },
{ id: 3, name: 'Milk', price: 3.49 }
];
<ShoppingList items={groceries} />
Advanced List with Actions
function TaskList({ tasks, onToggle, onDelete }) {
return (
<div>
{tasks.map(task => ( <div key={task.id} className="task-item">
<input type="checkbox" checked={task.completed} onChange={() => onToggle(task.id)} />
<span className={task.completed ? 'completed' : ''}> {task.title} </span>
<button onClick={() => onDelete(task.id)}> Delete </button>
</div>
))}
</div> );
}
Key Best Practices
// ✅ Good: Use unique, stable IDs
{items.map(item => ( <Item key={item.id} data={item} /> ))}
// ❌ Avoid: Using array index when order can change
{items.map((item, index) => ( <Item key={index} data={item} /> ))}
// ✅ Acceptable: Index as key only when list is static
{staticItems.map((item, index) => ( <Item key={index} data={item} /> ))}
useEffect: Handling Side Effects
The useEffect hook lets you perform side effects in function components.
Basic useEffect Usage
import React, { useState, useEffect } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds(prevSeconds => prevSeconds + 1);
}, 1000);
// Cleanup function
return () => clearInterval(interval);
}, []); // Empty dependency array = runs once on mount
return <div>Timer: {seconds} seconds</div>;
}
useEffect Dependency Patterns
1. Run on Every Render
useEffect(() => {
console.log('Runs after every render');
});
2. Run Only on Mount
useEffect(() => {
console.log('Runs only once after mount'); }, []); // Empty dependency array
3. Run When Specific Values Change
useEffect(() => {
console.log('Runs when count changes'); }, [count]); // Dependency array with count
Data Fetching with useEffect
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => { const fetchUser = async () => {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error('Failed to fetch user');
}
const userData = await response.json();
setUser(userData);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
} };
fetchUser();
}, [userId]); // Re-fetch when userId changes
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return <div>User not found</div>;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
); }
Virtual DOM & Reconciliation
React uses a Virtual DOM to optimize updates to the real DOM.
How It Works
1.Virtual DOM Creation: React creates a lightweight copy of the real DOM in memory
2.Diffing: When state changes, React compares the new Virtual DOM with the previous version
3.Reconciliation: React calculates the minimum changes needed
4.DOM Update: Only the changed elements are updated in the real DOM
The Diffing Algorithm
React's diffing algorithm makes two key assumptions:
- Different element types produce different trees
- Keys help identify which elements have changed, moved, or been removed
// This change will completely rebuild the subtree
<div> → <span>
<Counter /> <Counter />
</div> </span>
// This change will preserve the Counter component
<div> → <div>
<Counter /> <Counter />
</div> <Timer />
</div>
Performance Benefits
- Batched Updates: React batches multiple state changes
- Efficient Rendering: Only necessary DOM manipulations
- Predictable Updates: Deterministic rendering based on state
Styling in React
React offers multiple approaches to styling components.
1. CSS Stylesheets
styles.css
.card {
background: white;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.card-title {
font-size: 1.25rem;
font-weight: bold;
margin-bottom: 8px;
}
Component.jsx
import './styles.css';
function Card({ title, children }) {
return (
<div className="card">
<h2 className="card-title">{title}</h2>
{children}
</div>
);
}
Styled Components (CSS-in-JS)
import styled from 'styled-components';
const StyledButton = styled.button` padding: 12px 24px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; background-color: ${props => props.primary ? '#007bff' : '#6c757d'};
color: white; &:hover { opacity: 0.8;}`;
function Button({ primary, children }) {
return (
<StyledButton primary={primary}> {children} </StyledButton>
);
}
Best Practices
Component Design
1.Single Responsibility - Each component should have one clear purpose
2.Composition over Inheritance - Combine small components to create complex UIs
3.Props Interface Design - Make props clear and well-documented
4.Error Boundaries - Handle errors gracefully
State Management
1.Keep state local - Only lift state up when necessary
2.Use functional updates - setState(prev => prev + 1) instead of setState(count + 1)
3.Avoid deeply nested state - Flatten state structure when possible
Performance
1.Use React.memo for components that don't need frequent re-renders
2.Optimize useEffect dependencies - Be specific about what triggers effects
3.Implement proper key props - Help React optimize list rendering
Code Organization
1.Consistent naming - Use PascalCase for components, camelCase for functions
2.File structure - Group related files together
3.Import organization - External libraries first, then internal modules
What's Next?
Now that you understand React fundamentals, here are your next steps:
Intermediate Topics
- React Router - Navigation between pages
- Context API - Global state management
- Custom Hooks - Reusable stateful logic
- Error Boundaries - Graceful error handling
Advanced Topics
- Performance Optimization - React.memo, useMemo, useCallback
- State Management Libraries - Redux, Zustand
- Testing - Jest, React Testing Library
- TypeScript - Type safety for React
Build Projects
- Todo List App
- Weather Dashboard
- Shopping Cart
- Blog Platform
Conclusion
Congratulations! You've learned the fundamental concepts that power React applications:
✅ Declarative programming with JSX
✅ Component-based architecture
✅ State management with useState
✅ Event handling and user interactions
✅ Conditional rendering patterns
✅ List rendering with keys
✅ Side effects with useEffect
✅ Styling approaches in React
✅ Project structure best practices
React's component-based approach makes building complex UIs manageable by breaking them into smaller, reusable pieces. The declarative nature means you describe what you want, and React handles the how.
The counter app you built demonstrates core React concepts in action. From here, you can expand your skills by building more complex applications and exploring React's ecosystem.
Remember: React is a library, not a framework, which means you have the flexibility to choose additional tools as your projects grow. Start simple, practice regularly, and gradually add complexity as you become more comfortable with React's mental model. Happy coding!