This post is part of a new series where we build a clone of Airbnb with Next.js. See the first post here.

We’ll implement that in the homepage of the project, which is defined by the pages/index.js file that Next.js created for us.

Right now the content of that file contains some sample code. You can go ahead and remove some of that, and just leave this:

export default function Home() {
return <div></div>
}

We’ll add the rest later on.

Now let’s take a look at the original Airbnb stays listing, which at this time looks something like this:

I’d like to create something similar. Not pixel perfect, of course. I just want to implement similar functionality, but since I’m not a designer, I’m also going to borrow some of the style used here. We’ll adhere to that as much as possible without going too crazy about that.

The first thing we’re going to do is, we are going to define 2 houses. We’ll do that in a JavaScript file which I’m going to call houses.js, in the root folder of the project

Inside this file, we’ll define a houses array with 2 entries, which contain data I got from the Airbnb homepage I opened in my browser. You can add any data you want, of course. I copied the default picture of 2 houses and stored them in the public/img/ folder (create it in your project), with some details about the houses, which we’ll use to build the houses list:

houses.js

export default [
{
picture: '/img/1.jpg',
type: 'Entire house',
town: 'New York',
title: 'Beautiful flat in New York!'
},
{
picture: '/img/2.jpg',
type: 'Entire house',
town: 'Amsterdam',
title: 'A flat in Amsterdam with a great view'
}
]

I used the images from https://unsplash.com/photos/uY2kic9wlmc and https://unsplash.com/photos/sqc9yv6iueE, and I copied them in the public/img folder.

Tip: click the “Download free” button on Unsplash instead of right-clicking the image to get the image as jpg. Otherwise you’ll download it as webp

Files in the public folder are automatically served statically by Next.js. You can immediately see the house image by opening your browser at http://localhost:3000/img/1.jpg

Later on, we’ll extract houses from the database instead of this JSON file, but for the time being let’s stick to this static catalog.

Let’s switch back to our pages/index.js file.

Now we can import this JSON into our page component using the syntax

pages/index.js

import houses from '../houses.js'

Now we can iterate over those houses in our component JSX, wrapping all of them in a div with class houses:

pages/index.js

import houses from '../houses.js'

export default function Home() {
return (
<div>
<h2>Places to stay</h2>

<div className="houses">
{houses.map((house, index) => {
//...
})}
</div>
</div>
)
}

See https://flaviocopes.com/react-how-to-loop/ for more information on how to do a loop in JSX

Let’s now create a separate component to render the single house in the list.

Create a new components folder in the project root.

Let’s create a House.js file in here, and just return a basic “House” text from it:

components/House.js

export default function House() {
return (
<div>
<h2>House</h2>
</div>
)
}

In pages/index.js we can now import it:

pages/index.js

import House from '../components/House'

and we can use it inside the JSX to render it for every house in the houses array:

pages/index.js

{
houses.map((house, index) => {
return <House key={index} {...house} />
})
}

We pass all the house properties as props using the {...house} syntax.

We also add a key inside the list, because React wants that property in all lists, otherwise it will complain with warnings in the devtools.

This is what you should see now in the browser:

Now open the components/House.js file and accept the props argument, then log its content before returning the JSX:

components/House.js

export default function House(props) {
console.log(props)

return (
<div>
<h2>House</h2>
</div>
)
}

If you open the browser console, you should see this:

Now that we have those props coming in, we can render them in the output of the component:

components/House.js

export default function House(props) {
return (
<div>
<img src={props.picture} width="100%" alt="House picture" />
<p>
{props.type} - {props.town}
</p>
<p>{props.title}</p>
</div>
)
}

Note that I used a img tag instead of the Image component provided by Next.js via next/image because at this point I want to be able to set the image width to auto by omitting the height attribute, something that we can’t do with Image.

Now the home page of the site should look similar to this:

Using Next.js we have the ability to use styled-jsx in our components, to add scoped CSS (CSS that is applied only to the component it’s added to, and does not leak outside).

Let’s use CSS Grid to style this list a little bit better. In pages/index.js, add the following block inside the JSX:

pages/index.js

<style jsx>{
.houses {
display: grid;
grid-template-columns: 49% 49%;
grid-template-rows: 300px 300px;
grid-gap: 2%;
}
}</style>

Like this:

pages/index.js

import houses from '../houses.js'
import House from '../components/House'

export default function Home() {
return (
<div>
<h2>Places to stay</h2>

<div className="houses">
{houses.map((house, index) => {
return <House key={index} {...house} />
})}
</div>

<style jsx>{
.houses {
display: grid;
grid-template-columns: 49% 49%;
grid-template-rows: 300px 300px;
grid-gap: 2%;
}
}</style>
</div>
)
}

Also add this line of CSS:

body {
}
to the styles/global.css.