There is one problem with our Card
component from the previous lesson. The information in the card is hardcoded, which means all the articles will have the same image, same title, same description, and the same link.
That is not realistic at all. We need the cards to have the same layout and appearance, but they should contain different information.
However, as we just discussed, variables defined inside a component is private to that component. Other component don't have access to them. But in this case, we must pass data from a parent to a child component.
This is where props come in. It is a portal for us to pass data between components. For example,
1export default function App() {
2 const title = "Introduction to Next.js";
3 const description =
4 "Learn how to build scalable and performant websites using Next.js.";
5 const imageUrl = "https://via.placeholder.com/400x200";
6 const link = "/";
7
8 return (
9 <Card
10 imageUrl={imageUrl}
11 title={title}
12 description={description}
13 link={link}
14 />
15 );
16}
As you can see, the props can be passed to child components as if you are assigning attributes, except that the value needs to be wrapped inside a pair of curly braces ({}
). This is to make sure whatever is inside will be interpreted as JavaScript, allowing you to pass different types of data.
For instance, you can pass strings, numbers, Boolean values, or even something more complex such as arrays and objects.
1export default function App() {
2 const title = "Introduction to Next.js";
3 const description =
4 "Learn how to build scalable and performant websites using Next.js.";
5 const imageUrl = "https://via.placeholder.com/400x200";
6 const link = "/";
7
8 return (
9 <Card
10 imageUrl={imageUrl}
11 title={title}
12 description={description}
13 link={link}
14 />
15 );
16}
The props will be passed to Card
as an object. In fact, it is the only input argument your React component will receive. You can then read these props inside Card
like this:
1export default function Card({ imageUrl, title, description, link }) {
2 return (
3 <div className="bg-white shadow-md rounded-lg overflow-hidden">
4 <img src={imageUrl} alt={title} className="w-full h-48 object-cover" />
5 <div className="p-6">
6 <h2 className="text-2xl font-bold mb-2">{title}</h2>
7 <p className="text-gray-700 mb-4">{description}</p>
8 <a href={link} className="text-blue-500 hover:text-blue-700">
9 Read more
10 </a>
11 </div>
12 </div>
13 );
14}
Do not neglect the curly braces here, because it is a single object. This syntax is called object destructuring, which allows you directly assign the object's properties to variables.
Alternatively, you can assign the object to a single variable, and access individual properties using the dot operator (.
):
1export default function Card(props) {
2 return (
3 <div className="bg-white shadow-md rounded-lg overflow-hidden">
4 <img
5 src={props.imageUrl}
6 alt={props.title}
7 className="w-full h-48 object-cover"
8 />
9 <div className="p-6">
10 <h2 className="text-2xl font-bold mb-2">{props.title}</h2>
11 <p className="text-gray-700 mb-4">{props.description}</p>
12 <a href={props.link} className="text-blue-500 hover:text-blue-700">
13 Read more
14 </a>
15 </div>
16 </div>
17 );
18}
Rendering a grid of articles
Right now, we are only displaying one article, to display an article grid, we need to combine what we've discussed with the list rendering methods we talked about before.
Our Card
component remains unchanged in this case.
Card.jsx
1export default function Card({ imageUrl, title, description, link }) {
2 return (
3 <div className="bg-white shadow-md rounded-lg overflow-hidden">
4 <img src={imageUrl} alt={title} className="w-full h-48 object-cover" />
5 <div className="p-6">
6 <h2 className="text-2xl font-bold mb-2">{title}</h2>
7 <p className="text-gray-700 mb-4">{description}</p>
8 <a href={link} className="text-blue-500 hover:text-blue-700">
9 Read more
10 </a>
11 </div>
12 </div>
13 );
14}
But in the App.jsx
, you need to first get a list of articles from the database or other data sources.
App.jsx
1export default function App() {
2 const articles = [
3 {
4 id: 1,
5 title: "Introduction to Next.js",
6 description:
7 "Learn how to build scalable and performant websites using Next.js.",
8 imageUrl: "https://via.placeholder.com/400x200",
9 link: "/",
10 },
11 . . .
12 ];
13 return (
14 . . .
15 );
16}
In our example, we are hardcoding the data (articles
), but for now, let's pretend it is retrieved from the database.
And next, use the map()
method to convert the articles
array into a list of Card
s. Don't forget the key
attribute.
1export default function App() {
2 const articles = [. . .];
3 return (
4 <div className="container mx-auto px-4 py-8">
5 <h1 className="text-4xl font-bold mb-8 text-center">Blog Articles</h1>
6 <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
7 {articles.map((article) => (
8 <Card
9 key={article.id}
10 imageUrl={article.imageUrl}
11 title={article.title}
12 description={article.description}
13 link={article.link}
14 />
15 ))}
16 </div>
17 </div>
18 );
19}
Props with default values
Sometimes you may want to define a default value for the props. When no value is specified, it will fall back to the default. For example:
1export default function Img({ width = 500, height = 300, src, alt }) {
2 return <img src={src} alt={all} width={width}, height={height} />;
3}
In this case, when no width
or height
is specified, the default values 500
and 300
will be used.
children, a special prop that allows you to nest components
Besides these custom props defined by you, there is a special reserved prop called children
. It is often used to pass JSX between components. This allows you to create a nested structure similar to how you would nest HTML elements.
Nested HTML elements
1<div>
2 <img src=". . ." alt=". . ." />
3
4 <div>
5 <h2>Hello, world!</h2>
6 <p>Lorem ipsum. . .</p>
7 <a href=". . .">Read more</a>
8 </div>
9</div>
Nested JSX components
1<Card imageUrl={imageUrl} title={title} link={link}>
2 Lorem ipsum dolor sit amet,{" "}
3 <span className="font-bold">consectetur adipisicing elit</span>. Nisi pariatur
4 cupiditate, blanditiis dolorum sapiente distinctio ratione quos facere
5 eligendi laudantium numquam quaerat vero quia animi voluptatum? Atque porro
6 labore doloremque.
7</Card>
In this case, whatever is wrapped inside <Card></Card>
will be passed to the Card component as the prop children, which can then be rendered like this:
1export default function Card({ imageUrl, title, link, children }) {
2 return (
3 <div className="bg-white shadow-md rounded-lg overflow-hidden">
4 <img src={imageUrl} alt={title} className="w-full h-48 object-cover" />
5 <div className="p-6">
6 <h2 className="text-2xl font-bold mb-2">{title}</h2>
7 <p className="text-gray-700 mb-4">{children}</p>
8 <a href={link} className="text-blue-500 hover:text-blue-700">
9 Read more
10 </a>
11 </div>
12 </div>
13 );
14}