What we did up to now is pretty cool!
In this lesson we’ll add a navigation bar to the page, and we’ll also add some styling to make the app look nicer.
First, I’m going to add a logo image in public/img/logo.png
. It reminds the Airbnb logo, just upside down and with a different color.
Feel free to use any image you want.
I’m going to include this image in the header, which will be present in every page.
Let’s create a new file in the components
directory, called Layout.js
.
In there, we’ll create the shell of our application: we’ll include it in every page component to provide common UI, including basic CSS and the heading.
In this component we take the props, and we render the content
props in a main
tag:
components/Layout.js
const Layout = props => {
return (
<div>
<main>{props.content}</main>
</div>
)
}
export default Layout
Then in the pages/index.js
and pages/houses/[id].js
we import this Layout component, and we return that component, passing the JSX as its content
prop:
pages/index.js
//...
import Layout from '../components/Layout'
const content = (
<div>
<h2>Places to stay</h2>
{ /* all the rest of the JSX that we had in `pages/index.js` */}
</div>
)
const Index = () => <Layout content={content} />
export default Index
See? We have wrapped our JSX in a content
variable, and we pass that to the Layout component content
prop, so we can access it in components/Layout.js
.
Now we can do the same for the other page component we have, pages/houses/[id].js
. In there, we do things a little differently: instead of creating a content
variable, we directly put the component JSX inside the content
prop:
pages/houses/[id].js
//...
import Layout from '../../components/Layout'
const House = props => (
<Layout
content={
<div>
<Head>
<title>{props.house.title}</title>
</Head>
<img src={props.house.picture} width='100%' alt='House picture' />
<p>
{props.house.type} - {props.house.town}
</p>
<p>{props.house.title}</p>
<p>
{props.house.rating} ({props.house.reviewsCount})
</p>
</div>
}
/>
)
//...
Why? Because in this component JSX we access the props values, so we can’t define the JSX outside of the component. I could have also written
pages/houses/[id].js
const House = props => {
const content = (
<div>
<Head>
<title>{props.house.title}</title>
</Head>
<img src={props.house.picture} width='100%' alt='House picture' />
<p>
{props.house.type} - {props.house.town}
</p>
<p>{props.house.title}</p>
<p>
{props.house.rating} ({props.house.reviewsCount})
</p>
</div>
)
return (
<Layout
content={content} />
)
}
but it’s mostly the same, and I think a little bit more complicated to grasp at a first look.
Cool! So now we have both pages use the Layout component as their base.
We can now go back to it, and we add a little bit of CSS, using styled-jsx
:
First we add a global CSS to style the body tag:
components/Layout.js
const Layout = props => {
return (
<div>
<main>{props.content}</main>
<style jsx global>{`
body {
margin: 0;
font-family: Roboto, -apple-system, BlinkMacSystemFont, Segoe UI,
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue,
sans-serif;
font-size: 14px;
line-height: 1.5;
color: #333;
}
`}</style>
</div>
)
}
Then I also add some scoped CSS to style the main
tag:
components/Layout.js
const Layout = props => {
return (
<div>
<main>{props.content}</main>
<style jsx global>{`
body {
margin: 0;
font-family: Roboto, -apple-system, BlinkMacSystemFont, Segoe UI,
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue,
sans-serif;
font-size: 14px;
line-height: 1.5;
color: #333;
}
`}</style>
<style jsx>{`
main {
position: relative;
max-width: 56em;
background-color: white;
padding: 2em;
margin: 0 auto;
box-sizing: border-box;
}
`}</style>
</div>
)
}
Now, I’m going to add a header to the page. I’ll do that in a separate component, which I call Header.js
, and I place it into the components
folder.
Here’s a start, we include the logo and a first nav
container for a couple links we’re going to add later:
components/Header.js
const Header = () => (
<div className='nav-container'>
<img src='/img/logo.png' alt='' />
<nav>
</nav>
<style jsx>{`
.nav-container {
border-bottom: 1px solid #eee;
height: 50px;
}
img {
float: left;
}
`}</style>
</div>
)
export default Header
Now let’s add those links I was talking about. I add two links, one is Sign up and the other is Log in. We’ll use them soon. I also make the logo link to the home page. I also add some CSS to style them nicely:
components/Header.js
import Link from 'next/link'
const Header = () => (
<div className='nav-container'>
<Link href='/'>
<a>
<img src='/img/logo.png' alt='' />
</a>
</Link>
<nav>
<ul>
<li>
<Link href='/register'>
<a>Sign up</a>
</Link>
</li>
<li>
<Link href='/login'>
<a>Log in</a>
</Link>
</li>
</ul>
</nav>
<style jsx>{`
ul {
margin: 0;
padding: 0;
}
li {
display: block;
float: left;
}
a {
text-decoration: none;
display: block;
margin-right: 15px;
color: #333;
}
nav a {
padding: 1em 0.5em;
}
.nav-container {
border-bottom: 1px solid #eee;
height: 50px;
}
img {
float: left;
}
ul {
float: right;
}
`}</style>
</div>
)
export default Header
Now in Layout.js
, add
import Header from './Header'
and inside the JSX:
const Layout = props => {
return (
<div>
<Header />
<main>{props.content}</main>
...
Awesome! This should be the end result so far:
The code for this lesson is available at https://github.com/flaviocopes/airbnb-clone-react-nextjs/commit/042e665f9c50d4f12e2d45ce769246a735feeb01