This content originally appeared on flaviocopes.com and was authored by flaviocopes.com
This post is part of a new series where we build a clone of Airbnb with Next.js. See the first post here.
- Part 1: Let’s start by installing Next.js
- Part 2: Build the list of houses
- Part 3: Build the house detail view
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
export default function Layout(props) {
return (
<div>
<main>{props.content}</main>
</div>
)
}
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 houses from '../houses.js'
import House from '../components/House'
import Layout from '../components/Layout'
const content = (
<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>
)
export default function Home() {
return <Layout content={content} />
}
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 Head from 'next/head'
import houses from '../../houses.js'
import Layout from '../../components/Layout'
export default function House(props) {
return (
<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>
</div>
}
/>
)
}
export async function getServerSideProps({ query }) {
const { id } = query
return {
props: {
house: houses.filter((house) => house.id === parseInt(id))[0]
}
}
}
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
export default function 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>
</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
:
components/Layout.js
export default function Layout(props) {
return (
<div>
<main>{props.content}</main>
<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
export default function Header(props) {
return (
<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>
)
}
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'
export default function Header(props) {
return (
<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>
)
}
Now in Layout.js
, import and include the Header
component:
import Header from './Header'
export default function Layout(props) {
return (
<div>
<Header />
<main>{props.content}</main>
<style jsx>{`
main {
position: relative;
max-width: 56em;
background-color: white;
padding: 2em;
margin: 0 auto;
box-sizing: border-box;
}
`}</style>
</div>
)
}
Awesome! This should be the end result so far:
This content originally appeared on flaviocopes.com and was authored by flaviocopes.com
flaviocopes.com | Sciencx (2021-12-04T05:00:00+00:00) Airbnb clone, CSS and navigation bar. Retrieved from https://www.scien.cx/2021/12/04/airbnb-clone-css-and-navigation-bar/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.