I want to start with creating the list of houses view.
We’ll implement that in the homepage of the project, which is defined by the pages/index.js
file we created in the last lesson.
Now let’s take a look at the Airbnb stays listing:
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.json
, in the pages
folder (create it).
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/houses/
folder (create it in your project), with some details about the houses, which we’ll use to build the houses list:
pages/houses.json
[
{
"picture": "/img/houses/1.jpg",
"type": "Entire house",
"town": "Ostuni",
"title": "Beautiful flat in Ostuni!",
"rating": 4.93,
"reviewsCount": 198
},
{
"picture": "/img/houses/2.jpg",
"type": "Entire house",
"town": "Isla Mujeres",
"title": "The World Famous Seashell House ~ Casa Caracol",
"rating": 4.77,
"reviewsCount": 246
}
]
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/houses/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.json'
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.json'
const Index = () => (
<div>
<h2>Places to stay</h2>
<div className='houses'>
{houses.map((house, index) => {
//...
})}
</div>
</div>
)
export default Index
Let’s now create a separate component to render the single house in the list.
We’ll name it House
, and we’ll store it in a file named House.js
in new components
folder in the project root.
Let’s create this file and just return a basic “House” text from it:
components/House.js
const House = () => (
<div>
<h2>House</h2>
</div>
)
export default House
That is an implicit return, a feature of arrow functions. Look for “implicit return” on https://flaviocopes.com/javascript-arrow-functions/
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.
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
const House = props => {
console.log(props)
return (
<div>
<h2>House</h2>
</div>
)
}
export default House
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
const House = props => {
return (
<div>
<img src={props.picture} width='100%' alt='House picture' />
<p>
{props.type} - {props.town}
</p>
<p>{props.title}</p>
<p>
{props.rating} ({props.reviewsCount})
</p>
</div>
)
}
export default House
Now the home page of the site should look similar to this:
Note that the images I used are copyrighted and I will not include them in the project source code, but you can add any image in public/img/houses/1.jpg
and public/img/houses/2.jpg
to suit the sample code. Create the public/img/
as it does not exist by default.
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: 50% 50%;
grid-template-rows: 300px 300px;
grid-gap: 40px;
}
`}</style>
Like this:
pages/index.js
import houses from './houses.json'
import House from '../components/House'
const Index = () => (
<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: 50% 50%;
grid-template-rows: 300px 300px;
grid-gap: 40px;
}
`}</style>
</div>
)
export default Index
This should be the result of all our work in this module:
The code for this lesson is available at https://github.com/flaviocopes/airbnb-clone-react-nextjs/commit/747599bfd139c5910ef0ac2f4533a1a11778dd1b