
At DETL, we build software applications that blend robust engineering with elegant design. Whether it’s mobile or web applications, we focus on delivering exceptional user experiences without compromising on technical excellence.
Today, we’re sharing our comprehensive guide to React — one of the most powerful tools in modern web development. To ensure this guide is accessible to developers of all levels, you’ll find a “Definitions” section at the end explaining key technical terms. Terms highlighted in “bold” throughout the blog post have definitions in the definitions section.
For any questions or concerns, feel free to email me at hello@detl.ca or visit DETL’s website. Without further ado, here is the developers guide to React
Introduction
React has revolutionised how we build web applications, but its ecosystem is filled with terms like “reconciliation”, “composition” and “error boundaries” that can overwhelm newcomers. In this blog post we will break down these concepts in a way that actually makes sense.
1. The Building Blocks: Components
At its core, React is all about components. Think of them like Lego pieces that you can reuse to build your application’s interface. A button, an input field, or even an entire page can be a component. What makes components powerful is their reusability and composability — you can create them once and use them throughout your application.
Components in React are just Javascript functions that return markup. This declarative approach means you describe what should appear, not how it should be created. You can pass data between components, nest them within each other, and build complex UIs from simple, isolated pieces. This architecture makes your code more maintainable, testable, and logical to follow.
functionButton() {
return <button>Click me!</button>;
}

2. JSX: Javascript in Disguise
When you write React, you’re not writing HTML — you’re writing JSX. JSX is short for “Javascript XML” which is a syntax extension that allows you to write HTML-like syntax directly in a javascript file.
JSX essentially gets compiled into a regular Javascript function calls. As an example, behind the scenes, <div>hello</div>
becomes React.createElement("div",null,"Hello")
which gets called as a function and it creates a div
container on the webpage. This transformation happens during the build process, so your browser never sees the JSX syntax.
In the context of JSX and React, when your JSX code is compiled, it gets transformed into React.createElement()
function calls, which are what actually create the elements you see on the page.
JSX’s true power comes from its ability to embed dynamic Javascript values. Anything with curly braces {}
is evaluated as Javascript, allowing you to insert variables, call functions, or even include conditional logic and loops directly in your markup. This makes your UI truly dynamic and responsive to changing data.
functionGreeting({ name }) {
return <h1>Hello, {name}!</h1>;
}
3. Props: Passing Data Between Components
Props are React’s way of communicating between components. They work like custom HTML attributes, allowing you to pass any data — even other components — from parent to child. Props flow in one direction (parent to child), or in the other direction but for the sake of this blog let’s focus on the flow from parent → child. This creates a predictable data flow that makes application easier to understand and debug.
When a parent component renders a child component, it can pass any number of props to it. These props become properties on the props object that the child component receives. The child can then use these values to customise its appearance and behavior.
Props can be primitive values like strings and numbers, but they can also be complex objects, arrays or even functions. This flexiblity lets you build highly reusable components that can adapt to different contexts and requirements throughout your application.
functionParent() {
return <Child message="Hello from parent!" onAction={() => console.log('Child acted!')} />;
}
functionChild({ message, onAction }) {
return <p onClick={onAction}>{message}</p>;
}
4. The Children Prop: Composition Made Easy
One special prop, children
, makes component composition incredibly flexible. This prop represents whatever content is placed between the opening and closing tags of a component. It’s React version of the “slot” pattern found in other frameworks. A “slot” pattern is a way to make components more flexible by allowing them to receive and render any content that is passed between their tags.
The children prop allows you to create a “wrapper” component that apply consistent styling, behavior, or layout to varying content. This approach is more flexible than inheritance and encourages composition over configuration.
This compositional pattern helps you avoid prop drilling (passing props through multiple levels) and creates cleaner, more intuitive API’s for your component. It’s one of the key ways React encourages you to think in terms of composition rather than complex inheritance hierarchies.
functionCard({ children, title }) {
return (
<div className="card">
<h2 className="card-title">{title}</h2>
<div className="card-content">{children}</div>
</div>
);
}
// Usage
<Card title="Important Information">
<p>This is the card content.</p>
<button>Learn More</button>
</Card>
5. Rendering: How React Updates the DOM
React doesn’t directly manipulate the DOM (Document Object Model) like JQuery. Instead, it uses a “Virtual DOM” as an intermediary layer, which is lightweight Javascript representation of the actual DOM.
The virtual DOM is essentially a tree of Javascript objects that mirror the structure of your UI. Each object represents an element with its properties and children. When your application state changes, React creates a new Virtual DOM true representing the updated UI. This process is much faster than working with the actual DOM because it happens entirely in memory without triggering the browser to re-render.
React then compares this new virtual DOM with the previous version through a process called “diffing”. Using sophisticated algorithm, React identifies exactly what changed between the two versions. Once it knows what’s different, React performs a process called “reconciliation”, where it efficiently updates only the part of the real DOM that need to change.
This approach dramatically improves performance by:
- Batching multiple DOM updates together,
- Avoiding un-necessary DOM operations,
- Minimizing browser re-render,
- Providing consistent, predictable rendering process
The Virtual DOM acts as a buffer between your code and the browser handling all complex DOM manipulations.
6. State: Managing Component Data
State represents data that changes over time in your application. Unlike regular Javascript variables, updating state causes React to re-render your component, reflecting changes in the UI. This reactive nature is at the heart of what makes React so poweful.
In functional components, state is managed through the useState hook. When you call useState, you provide an initial value and get back two things:
- The current state value and,
- Function to update it.
The update function doesn’t immediately change the state value, instead, it schedules a re-render with the new value.
State should be kept as minimal as possible, containing only the essential data your component needs. Derived values should be calculated during rendering rather than store in state. Additionally, you should keep state as close as possible to where it is used, lifting it up only when necessary to share between components to avoid re-rendering of components as state changes are the number 1 performance killers!
import { useState } from'react';
functionCounter() {
const [count, setCount] = useState(0);
// count is the initial value
// setCount is the function to update it.
// Correct way to update state based on previous state
functionincrement() {
setCount(prevCount => prevCount + 1);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={increment}>Click me</button>
</div>
);
}
7. Hooks: Superpowers for Function Components
Hooks let you use React features like state without writing class components. They were introduced in React 16.8 to solve various problems with class components and provide a more direct API to React features. Hooks follow specific rules: they can only be called at the top level of a functional components or custom hooks, and they can’t be called conditionally.
Here are the most common hooks:
- useState: Manages component state by providing a state variable and a setter function. It accepts an initial value and returns the current state and a function to update it. Each call to useState creates an independent piece of state.
- useEffect: Handles side effects like data fetching, subscriptions, or DOM manipulations. It takes a function to run after render and an optional dependency array that controls when the effect runs. An empty dependency array means the effect runs once after the initial render, while no dependency array means it runs after every render.
- useRef: Creates a mutable reference that persists across renders. Unlike state, updating a ref doesn’t trigger a re-render. It’s perfect for accessing DOM elements directly or storing values that don’t affect rendering.
- useContext: Accesses data from a React context without prop drilling. It accepts a context object created by React.createContext and returns the current context value as determined by the nearest provider.
- useMemo: Memoizes expensive computations so they only re-run when dependencies change. It’s crucial for optimizing performance in components that perform complex calculations.
- useCallback: Memoizes callback functions to prevent unnecessary re-renders of child components that rely on reference equality. It’s similar to useMemo but specifically for functions.
- useReducer: Manages complex state logic with a reducer function, similar to Redux. It’s preferable to useState when state transitions depend on previous state or when multiple state values are updated together.
The built-in hooks can be combined to create custom hooks, allowing you to extract and reuse stateful logic between components. Custom hooks are functions that start with “use” and can be imported in other components to be called.
8. Effects: Reaching Outside React
Sometimes you need to interact with systems beyond React — like browser APIs or external services. The useEffect
hook is perfect for this, allowing you to perform side effects in functional components.
Effects run after render, ensuring the DOM is updated before your effect code executes. This timing is important when your effect needs to interact with the DOM or depends on layout calculations. Effects can be configured to run as follows:
- After every render,
- Only when specific dependencies change or,
- Just once when the component mounts.
A crucial aspect of useEffect is its cleanup mechanism. By returning a function from your effect, you provide React with a way to clean up resources when the component unmounts or before the effect runs again. This prevents any memory leaks and it ensures proper management of the useEffect resources.
Here are the following common use cases for useEffect
:
- Data fetching from APIs,
- Setting up and tearing down subscriptions,
- Manually changing the DOM,
- Integrating with third party libraries
Effects should be focused and specific — prefer multiple effects with clear dependencies over a single effect that does multiple unrelated things. This makes it much easier for your code to understand and maintain.
useEffect(() => {
// Setup phase - runs after render
const subscription = api.subscribe(data => {
setExternalData(data);
});
document.title = `${user.name}'s Profile`;
// Cleanup phase - runs before unmount or before effect re-runs
return() => {
subscription.unsubscribe();
};
}, [user.id]); // Only re-run if user.id changes
9. Context: Props Without the Drilling
Context solves the problem of passing props through many levels of components. It’s like creating a secret tunnel from one component to another far down the tree, avoiding the need to pass props through every intermediate component.
The Context API consists of three main parts:
- The context object created with
React.createContext()
- Provider component that makes the data available and,
- consumers that read the data.
Context is perfect for truly global data like themes, user information, or localization settings. However, it shouldn’t be overused — props are still the recommended way to pass data for most cases. Overusing context can make components reusability more difficult and create performance bottlenecks.
When a context value changes, all components that consume that context will re-render, regardless whether they actually use the specific part of the context that changed. For complex state manegement needs, you might combine context with useReducer or even consider state management like Redux.
// Create context with default value
constThemeContext = createContext('light');
// Provider in parent
functionApp() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<MainContent />
</ThemeContext.Provider>
);
}
// Consumer in deeply nested child
functionButton() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<>
<button className={theme}>Click Me</button>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
</>
);
}
10. Advanced Patterns: Portals, Suspense, and Error Boundaries
As your apps grow more complex, these advanced features become invaluable:
- Portals: Portals let you render components anywhere in the DOM tree, regardless of their position in the React component hierarchy. They are essential for creating modal dialogs, tooltips and popovers that need to visually escape the main container.
// Render a modal to a specific DOM node outside the main app hierarchy
returnReactDOM.createPortal(
<ModalContent />,
document.getElementById('modal-root')
);
- Suspense: Suspense improves the user experience by showing loading indicators while components or data load. It allows your UI to “wait” for resources to be displayed as a fallback until they are ready.
<Suspense fallback={<Spinner />}>
<LazyLoadedComponent />
<DataComponent />
</Suspense>
- Error Boundaries: Catch Javascript errors in their child component tree and display a fallback UI instead of crashing your entire application. It is important to understand that they don’t catch errors in event handlers, asynchronous code or their own rendering code, but they provide safety net for everything else.
All of these advanced patterns allow you to handle complex UI requirements while maintaining clean component architecture. They solve specific problems that arise in larger applications, from managing loading states to recovering from errors.
Putting It All Together
Understanding these core React concepts gives you the foundation to build powerful, maintainable user interfaces. The beauty of React lies in how these simple pieces combine to create complex applications while keeping your code organised and predictable.
React’s mental model emphasizes component composition, unidirectional data flow, and declarative UI updates. By embracing all of these principles, you can create applications that are easier to understand, debug, extend and build upon over time. The component-based architecture lets you build resuable pieces that can be combined in countless ways, while hooks and context provide powerful tools for managing state and side effects.
As your application grows, you’ll discover how these patterns complement each other. You might use context to provide theme data, portals to render modals and error boundaries to catch unexpected issues. The key is understanding when to apply each tool and how they work together to solve real-world problems.
Next time you’e building a React application, consider how these patterns can help solve your specific challenges. with practise, what once seemed confusing will become second nature in your developer toolkit.
THANK YOU FOR READING — VISIT MY LINKEDIN FOR ANY QUESTIONS OR JUST TO STOP BY AND SAY HI! Muaz Saleh
Definitions
- Reusability: In React, reusable components are UI building blocks that can be used multiple times across your application. Instead of creating a similar structure repeatedly, you abstract the shared logic, structure, and design into a single component, which can be reused with slight variations. (Source)
- Composability: “Composability refers to the ability for different components or elements to be combined or connected in various ways to create larger, more complex systems or structures. It’s a concept often used in the context of software development, computer systems, and engineering, but it applies to other fields as well.” (Source)
- Inheritance: Inheritance is an object-oriented programming concept where a class can inherit properties and methods from another class.