Think about this scenario, you are creating a blog app, and you need to create a list of articles, where each article is displayed using a card like this:
These cards have the same style but contain different information. As you can imagine, if you hardcode everything, there will be a lot of repetitions.
So, what if you can isolate this card component, and reuse it when needed?
How to create a component
This is what the component system is designed for in React. It provides you with a way to modularize different parts of the application, breaking it into reusable pieces, and making your code cleaner and more maintainable.
To create a new component, first create a components
directory inside the src
folder if it doesn't already exist. Then, navigate to the components
directory and create a new file named Card.jsx
.
1.
2├── README.md
3├── index.html
4├── package-lock.json
5├── package.json
6├── public
7├── src
8│ ├── App.css
9│ ├── App.jsx
10│ ├── assets
11│ │ └── react.svg
12│ ├── components
13│ │ └── Card.jsx <===
14│ ├── index.css
15│ └── main.jsx
16└── vite.config.js
src/components/Card.jsx
1function Card() {
2 return (
3 <div className="bg-white shadow-md rounded-lg overflow-hidden">
4 <img
5 src="https://via.placeholder.com/400x200"
6 alt="Article Image"
7 className="w-full h-48 object-cover"
8 />
9 <div className="p-6">
10 <h2 className="text-2xl font-bold mb-2">Article</h2>
11 <p className="text-gray-700 mb-4">Lorem ipsum dolor. . .</p>
12 <a href="#" className="text-blue-500 hover:text-blue-700">
13 Read more
14 </a>
15 </div>
16 </div>
17 );
18}
19
20export default Card;
This Card
component returns a piece of JSX code, which can then be imported and used inside App
like this:
App.jsx
1import Card from "./components/Card.jsx";
2
3function App() {
4 return (
5 <>
6 <Card />
7 <Card />
8 <Card />
9 <Card />
10 </>
11 );
12}
13
14export default App;
So, as you can see, a component is just a function module that can be exported and then imported into other components.
Except it follows two rules.
First, it is conventional to capitalize the first letter of each word when naming these functions, such as UploadButton
, NavBar
, and so on.
And second, the component should return a piece of JSX code.
Importing & exporting components
Since components are just regular functions, that means all the rules and features that apply to functions in JavaScript also applies to React components. For example, we can export a component in two different ways, default export and named export.
The previous example demonstrates the default export method, which means this Card
component can then be imported into other components like this:
src/App.jsx
1import Card from "./components/Card";
2
3function App() {
4 return <Card />;
5}
6
7export default App;
React allows you to omit the .jsx
or .js
file extensions when importing components.
Besides default export, you can also utilize the named export method, which enables you to export multiple components in one JSX file. For example,
src/components/Card.jsx
1function Card1() {
2 return <div>. . .</div>;
3}
4
5function Card2() {
6 return <div>. . .</div>;
7}
8
9export { Card1, Card2 };
src/App.jsx
1import { Card1, Card2 } from "./components/Card";
2
3function App() {
4 return (
5 <>
6 <Card1 />
7 <Card2 />
8 </>
9 );
10}
11
12export default App;
Variables and functions in components
You can also define variables inside a component, and the variables can be interpolated inside JSX using a pair of curly braces ({}
).
1function Card() {
2 const imageUrl = "./image.png";
3 const title = "Lorem ipsum dolor sit amet consectetur adipisicing elit";
4 const description = "Lorem ipsum dolor sit amet consectetur adipisicing elit";
5 const link = "/link-to-article";
6
7 return (
8 <div className="bg-white shadow-md rounded-lg overflow-hidden">
9 <img src={imageUrl} alt={title} className="w-full h-48 object-cover" />
10 <div className="p-6">
11 <h2 className="text-2xl font-bold mb-2">{title}</h2>
12 <p className="text-gray-700 mb-4">{description}</p>
13 <a href={link} className="text-blue-500 hover:text-blue-700">
14 Read more
15 </a>
16 </div>
17 </div>
18 );
19}
20
21export default Card;
Similar to defining variable, it is possible to define functions inside components as well. For example, here is a helper function that capitalizes the title.
1function Card() {
2 function capitalize(title) {
3 return title
4 .split(" ")
5 .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
6 .join(" ");
7 }
8
9 const imageUrl = "./image.png";
10 const title = "Lorem ipsum dolor sit amet consectetur adipisicing elit";
11 const description = "Lorem ipsum dolor sit amet consectetur adipisicing elit";
12 const link = "/link-to-article";
13
14 return (
15 <div className="bg-white shadow-md rounded-lg overflow-hidden">
16 <img src={imageUrl} alt={title} className="w-full h-48 object-cover" />
17 <div className="p-6">
18 <h2 className="text-2xl font-bold mb-2">{capitalize(title)}</h2>
19 <p className="text-gray-700 mb-4">{description}</p>
20 <a href={link} className="text-blue-500 hover:text-blue-700">
21 Read more
22 </a>
23 </div>
24 </div>
25 );
26}
27
28export default Card;
The variables and functions defined inside a component is private to that component, meaning they cannot be "seen" from the outside, and they are isolated within that component.
Component lifecycle
React components live through different stages, from their initial preparation, to rendering, and to their eventual unmounting. Collectively, these stages are known as the component lifecycle, which can be broken down into the following phases.
- Before rendering: Before the component renders, React will first execute some initialization code. This is where the component’s states are set up, props are received, and other initial configurations are made.
- Rendering: After initialization, React mounts the component. This phase involves the actual rendering of the component, and the DOM tree will be created.
- Updating: When the states or props change, the component enters the update phase. It will be updated and rerendered with the new information.
- Unmounting: When the component is no longer needed, it enters the unmounting phase, and will be removed from the DOM tree by React.
The built-in components
In addition to creating your own custom components, React offers a few built-in components that can be useful in certain circumstances.
Fragment
React requires each component to return only one root element, which means when you need to return multiple elements that are siblings, they must be wrapped under one parent node.
1<div>
2 <h1>Title in Fragment</h1>
3 <p>This paragraph is also within a fragment.</p>
4</div>
But, adding extra layers of DOM nodes can have a negative impact on the overall performance of your application.
Fragment (<></>
) lets you group multiple elements without adding extra nodes to the DOM. This is especially useful when you want to avoid unnecessary wrappers, and keep the DOM clean.
1<>
2 <h1>Title in Fragment</h1>
3 <p>This paragraph is also within a fragment.</p>
4</>
Profiler
The <Profiler>
component measures rendering performance of its children, helping you identify performance bottlenecks in your application.
1import { Profiler } from "react";
2
3function onRenderCallback(id, phase, actualDuration) {
4 console.log(`Profiler ID: ${id}`);
5 console.log(`Phase: ${phase}`);
6 console.log(`Actual Duration: ${actualDuration}`);
7}
8
9function ProfilerDemo() {
10 return (
11 <Profiler id="ProfilerDemo" onRender={onRenderCallback}>
12 <h1>This is a profiled component</h1>
13 </Profiler>
14 );
15}
16
17export default ProfilerDemo;
The onRenderCallback
event handler receives useful metrics such as the phase and the time taken to render, which can be very useful for debugging performance issues.
StrictMode
The <StrictMode>
does not render anything visible. It just enforces stricter checks during development, helping you identify potential issues in the application.
1import React from "react";
2import ReactDOM from "react-dom/client";
3import App from "./App.jsx";
4import "./index.css";
5
6ReactDOM.createRoot(document.getElementById("root")).render(
7 <React.StrictMode>
8 <App />
9 </React.StrictMode>
10);
<StrictMode>
only affects development builds and has no impact on production.
Suspense
<Suspense>
is one of the most important optimization tools in React we are going to discuss in detail later. It is used for handling asynchronous operations, such as lazy-loading components or fetching data.
1import { Suspense } from "react";
2
3const LazyComponent = React.lazy(() => import("./LazyComponent"));
4
5export default function SuspenseDemo() {
6 return (
7 <Suspense fallback={<div>Loading...</div>}>
8 <LazyComponent />
9 </Suspense>
10 );
11}
1export default function LazyComponent() {
2 return <h1>This component is lazily loaded!</h1>;
3}