Building a Card-Based Homepage Layout in React
The homepage of TinyHouse is to be to mostly presentational and aims to serve the purpose of telling the user what our app does as well as provide useful links to direct them elsewhere. In this lesson, we focus on building the presentational UI of the homepage.
Building the UI of the Homepage#
📝 The
cancun.jpg
image asset used in this lesson can be found - here.
📝 Thedubai.jpg
image asset used in this lesson can be found - here.
📝 Thelondon.jpg
image asset used in this lesson can be found - here.
📝 Thelos-angeles.jpg
image asset used in this lesson can be found - here.
📝 Themap-background.jpg
image asset used in this lesson can be found - here.
📝 Thesan-fransisco.jpg
image asset used in this lesson can be found - here.
📝 Thetoronto.jpg
image asset used in this lesson can be found - here.
The homepage we're going to build is to be to mostly presentational and aims to serve the purpose of telling the user what our app does as well as provide useful links to direct them elsewhere. There will be two child components we're going to create as part of the homepage:
The
<HomeHero />
component which is would be the upper portion of the homepage that is to have the search functionality and the different visual cards to help direct users to certain specific cities.The
<HomeListings/>
component which is the list of the highest priced listings we'll display to the user.
Everything else in the homepage will just be part of the <Home />
parent component.

The first thing we'll do as part as part of this lesson is to focus on the more presentational parts of the page while in the next lesson or so, we'll look to make the query for the recently added root level listings
field to give us the four highest priced listings.
Note: There are a couple of static image assets we'll need for the homepage that is to be kept in an
assets/
folder in thesrc/sections/Home/
directory. At the top of this lesson manuscript, you'll be able to find links to each of the image assets sources.
<HomeHero />
#
<HomeHero />
- Cards#
We'll work in the src/sections/Home/index.tsx
file to create the <Home />
component that's rendered in the index route of our app. The first thing we'll do is create the <HomeHero />
component file and an index.ts
file in a components/
folder within the src/sections/Home/
directory.
client/
src/
// ...
sections/
// ...
Home/
components/
HomeHero/
index.tsx
index.ts
// ...
// ...
In the src/sections/Home/components/index.ts
file, we'll re-export the <HomeHero />
component function we'll soon create.
export * from "./HomeHero";
In the src/sections/Home/components/HomeHero/index.tsx
file, we'll import the React
library and everything else we might need. We'll import all the components we'll need from Ant Design such as the <Card />
, <Col />
, <Input />
, <Row />
, and <Typography />
components. We'll import the image assets we'll use in this <HomeHero />
component from the assets/
directory in the parent Home /
directory - which is the image assets of the cities of toronto, dubai, los angeles, and london.
We'll destruct the <Title />
sub-component from <Typography />
and we'll destruct the <Search />
sub-component from <Input />
. And we'll export a component function named HomeHero
.
import React from "react";
import { Card, Col, Input, Row, Typography } from "antd";
import torontoImage from "../../assets/toronto.jpg";
import dubaiImage from "../../assets/dubai.jpg";
import losAngelesImage from "../../assets/los-angeles.jpg";
import londonImage from "../../assets/london.jpg";
const { Title } = Typography;
const { Search } = Input;
export const HomeHero = () => {};
In the <HomeHero />
component return statement, we'll return a parent <div />
element with two children - another <div />
element and the Ant Design <Row />
component. We'll apply a gutter spacing between each columns that is to be shown within the <Row />
.
import React from "react";
import { Card, Col, Input, Row, Typography } from "antd";
import torontoImage from "../../assets/toronto.jpg";
import dubaiImage from "../../assets/dubai.jpg";
import losAngelesImage from "../../assets/los-angeles.jpg";
import londonImage from "../../assets/london.jpg";
const { Title } = Typography;
const { Search } = Input;
export const HomeHero = () => {
return (
<div className="home-hero">
<div className="home-hero__search"></div>
<Row gutter={12} className="home-hero__cards"></Row>
</div>
);
};
The .home-hero__search
<div />
element will be responsible in displaying the <Title />
as well as the Search <Input />
where users will be able to search for listings in a certain city. The <Input />
component from Ant Design is fairly straightforward and essentially provides a text input where we can specify some additional variations. The <Search />
sub-component gives us the ability to have a search button alongside the input and gives us the capability to use a onSearch()
callback prop which gets triggered either when the user presses the "Enter"
key or clicks the search button.
With that said, in the <HomeHero />
component, let's add a <Title />
that says "Find a place you'll love to stay at"
and a <Search />
input that is to have a placeholder that says "Search San Fransisco"
(we're using San Fransisco as an example of a location that can be searched). In the <Search />
input, we'll provide a size
placeholder with a value of "large"
which helps specify we want a visibly large input. We'll also pass an "enterButton"
prop which helps display the search button alongside the input.
import React from "react";
import { Card, Col, Input, Row, Typography } from "antd";
import torontoImage from "../../assets/toronto.jpg";
import dubaiImage from "../../assets/dubai.jpg";
import losAngelesImage from "../../assets/los-angeles.jpg";
import londonImage from "../../assets/london.jpg";
const { Title } = Typography;
const { Search } = Input;
export const HomeHero = () => {
return (
<div className="home-hero">
<div className="home-hero__search">
<Title className="home-hero__title">Find a place you'll love to stay at</Title>
<Search placeholder="Search 'San Fransisco'" size="large" enterButton className="home-hero__search-input" />
</div>
<Row gutter={12} className="home-hero__cards"></Row>
</div>
);
};
Next, we'll build out a row of cards for the different cities we'll want the user to see in the home hero section. We'll essentially set up four separate columns and in md
and greater viewports, we'll want each of the columns to take 1/4th of the entire width of the viewport.
import React from "react";
import { Card, Col, Input, Row, Typography } from "antd";
import torontoImage from "../../assets/toronto.jpg";
import dubaiImage from "../../assets/dubai.jpg";
import losAngelesImage from "../../assets/los-angeles.jpg";
import londonImage from "../../assets/london.jpg";
const { Title } = Typography;
const { Search } = Input;
export const HomeHero = () => {
return (
<div className="home-hero">
<div className="home-hero__search">
<Title className="home-hero__title">Find a place you'll love to stay at</Title>
<Search placeholder="Search 'San Fransisco'" size="large" enterButton className="home-hero__search-input" />
</div>
<Row gutter={12} className="home-hero__cards">
<Col md={6}></Col>
<Col md={6}></Col>
<Col md={6}></Col>
<Col md={6}></Col>
</Row>
</div>
);
};
In small viewports (that is to say mobile viewports), we'll actually want to show just the first two columns side by side without showing the other two. With that said, we'll give the first two columns a width of 12
spaces to take half the available grid and the last two columns with values of 0
to declare we don't want it to be shown in small viewports.
import React from "react";
import { Card, Col, Input, Row, Typography } from "antd";
import torontoImage from "../../assets/toronto.jpg";
import dubaiImage from "../../assets/dubai.jpg";
import losAngelesImage from "../../assets/los-angeles.jpg";
import londonImage from "../../assets/london.jpg";
const { Title } = Typography;
const { Search } = Input;
export const HomeHero = () => {
return (
<div className="home-hero">
<div className="home-hero__search">
<Title className="home-hero__title">Find a place you'll love to stay at</Title>
<Search placeholder="Search 'San Fransisco'" size="large" enterButton className="home-hero__search-input" />
</div>
<Row gutter={12} className="home-hero__cards">
<Col xs={12} md={6}></Col>
<Col xs={12} md={6}></Col>
<Col xs={0} md={6}></Col>
<Col xs={0} md={6}></Col>
</Row>
</div>
);
};
For each of the columns, we'll display a <Card />
component where in the card cover
prop - we'll provide a src
for an accompanying image. We'll also look to display the appropriate label within the card to convey which city is is. The first one will refer to Toronto, followed by Dubai, Los Angeles, and then London.
We'll add an alt
prop (i.e. alternate text prop) to each card cover img
to comply with our React ESLint set-up and to explain what each of the images convey.
import React from "react";
import { Card, Col, Input, Row, Typography } from "antd";
import torontoImage from "../../assets/toronto.jpg";
import dubaiImage from "../../assets/dubai.jpg";
import losAngelesImage from "../../assets/los-angeles.jpg";
import londonImage from "../../assets/london.jpg";
const { Title } = Typography;
const { Search } = Input;
export const HomeHero = () => {
return (
<div className="home-hero">
<div className="home-hero__search">
<Title className="home-hero__title">Find a place you'll love to stay at</Title>
<Search placeholder="Search 'San Fransisco'" size="large" enterButton className="home-hero__search-input" />
</div>
<Row gutter={12} className="home-hero__cards">
<Col xs={12} md={6}>
<Card cover={<img alt="Toronto" src={torontoImage} />}>Toronto</Card>
</Col>
<Col xs={12} md={6}>
<Card cover={<img alt="Dubai" src={dubaiImage} />}>Dubai</Card>
</Col>
<Col xs={0} md={6}>
<Card cover={<img alt="Los Angeles" src={losAngelesImage} />}>Los Angeles</Card>
</Col>
<Col xs={0} md={6}>
<Card cover={<img alt="London" src={londonImage} />}>London</Card>
</Col>
</Row>
</div>
);
};
We want each of the cards shown in the homehero to be links to the listings page we'll eventually create where the listings for the appropriate city is then to be shown. If we recall, we've created a /listings/:location?
route that is to have a location
parameter that'll be used in the page to determine which listings to be shown for a certain location. At this moment, we're not concerned with how that location
parameter is going to be used to query for the correct listings - we'll just need to direct the user to the listings page and provide the appropriate URL parameter.
Since we want to link the user somewhere in our app, we'll import and use the <Link/>
component from react-router-dom.
import { Link } from "react-router-dom";
We'll then wrap each <Card />
in the columns with the <Link />
component and we'll provide a target path to take the user to the listings/:location?
page with the appropriate location in the route param.
For Toronto, we'll take them to
/listings/toronto
.For Dubai, we'll take them to
/listings/dubai/
.For Los Angeles, we'll take them to
/listings/los%20angeles
. The%20
value is a URL-safe character that is used to indicate a space between the wordslos
andangeles
.For London, we'll take them to
/listings/london/
.