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.

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two course and can be unlocked immediately with a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two
  • [00:00 - 00:14] The home page we're going to build is to be 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's going to be two child components we'll create as part of the home page.

    [00:15 - 00:35] We'll set up the home hero components, which is essentially the upper portion of the home page that is to have the search functionality and the different visual cards to help direct users to a few specific cities. And there's also going to be the home listings component, which is the list of the four highest price listings we'll display to the user.

    [00:36 - 00:47] And everything else would just be part of the home parent's component. Now the first thing we'll do as part of this lesson is focus on the more presentational parts of the page, such as the home hero and the rest of the page.

    [00:48 - 01:04] 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 price listings and we'll build the home listings component. We'll go from the top down, so the first thing we'll work on is the child home hero component.

    [01:05 - 01:24] Now there's a couple of static image assets we'll need for this page, so as a result, we've provided an assets folder in the home directory that has to have all the image assets we'll use. We'll provide these image assets in the shared code directory that we'll share with you as well as provide links to them in the lesson manuscript.

    [01:25 - 01:49] First thing we'll do is create the home hero component file and an index file in a components folder within the home directory. And in the components index file, we'll react sport the home hero component function we'll soon create.

    [01:50 - 02:03] We'll import the react library and everything else we might need. We'll import all the components we'll need from end design, such as the card, column, input, row and typography components.

    [02:04 - 02:32] And we'll import the image assets we'll use in this home hero component, which are 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.

    [02:33 - 02:52] And we'll export a component function named home hero. In the home hero component return statement, we'll return a parent div with two children, another div element and the end design row components.

    [02:53 - 03:20] And we'll apply a gutter or spacing between each columns within the row of 12. In this child div element, here's where we'll display the title, as well as the search input where users will be able to search for listings in a certain location.

    [03:21 - 03:45] The input component from end design is fairly straightforward and essentially provides a text input where we can control with some additional variations. The search sub-component gives us the ability to have a search button alongside the input and it provides a on-search callback prop, which gets triggered either when the user presses the inter key or clicks the search button.

    [03:46 - 04:05] The search input wheel setup will look very similar to the one shown here. With that said, let's add a title that says "Find a place you will love to stay at".

    [04:06 - 04:23] And a search input that is to have a placeholder that says "Search San Francisco". We could have said anything, but we're prompting the user to search for a city and we're just using San Francisco as an example.

    [04:24 - 04:41] And we'll provide a size placeholder with a value of large, which helps specify if you want a visibly large input. And we'll pass an inter button prop, which helps display the search button alongside the input, and we'll add an additional class that we've created for this.

    [04:42 - 05:03] We haven't used the on-search callback function yet to determine what would happen when a search is actually made, but we'll come back to this in a second. Next, we'll build out the row of cards for the different cities we'll want the user to see in the hero, and essentially we'll set up four separate columns for this.

    [05:04 - 05:22] In medium and greater viewports, we'll want each of these columns to take one- fourth of the entire width. In AntDesign, the entire grid width is given 24 units, so with that said, we can say each column will have a medium width of 6 units.

    [05:23 - 05:35] In small viewports, or that is to say, maybe mobile viewports, we'll actually just want to show the first two columns side by side without showing the other two. This is just the way we would want to set it up in our UI.

    [05:36 - 06:06] So with that said, we'll give the first two columns a width of 12 units to take half the available grid each, and the last two columns with values of 0 units to declare we don't want it to be shown in small or extra small viewports. For each of the columns, we'll display a card where the card cover prop will have an image element with a source that references one of the image assets we have imported in this file.

    [06:07 - 06:19] And within the card as well, we'll also look to just place a label to convey which city it is. So with that said, the first one would be Toronto, the greatest city in the world.

    [06:20 - 06:51] And followed by Dubai, Los Angeles, and London. Though we don't talk about accessibility in this course since it's not within the scope of this course, accessibility is an incredibly important piece for UI development.

    [06:52 - 07:10] In this particular case, ESLint tells us we should add an alt prop or an alternate text prop to basically explain what each of these images convey. So we'll follow along and we'll add the alt prop for each of these images to determine which city they're referring to.

    [07:11 - 07:40] And for the alt tags, I believe it's appropriate to use capital letters like as follows. We want each of these cards to be links that link us to the listings page will eventually create where listings for the appropriate city is shown.

    [07:41 - 07:59] Now, if we recall, we've created a listings route that is to have a location parameter that will be used in the page to determine which listings is to be shown for which location. At this moment, we're not concerned with how that location URL parameter is going to be used to query for the correct listings.

    [08:00 - 08:12] 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, we'll import and use the link component from React Router.

    [08:13 - 08:31] React Router done. And we'll wrap each card in the columns with the link component and we'll provide a target path to take the user to the listings page with the appropriate location in the route parameter.

    [08:32 - 08:44] So, for Toronto, we'll take them to listings/Toronto. Dubai will take them to /listings/ Dubai.

    [08:45 - 09:13] Los Angeles will take them to listings/Los Angeles. Now, the percent 20 value within our Los Angeles route is a URL safe character that is used to indicate a space between the words "loss" and "angeless".

    [09:14 - 09:23] That's the majority of what we'll want to do for the Home/Hero component. So, let's now import it and look to render it in the parent homepage component.

    [09:24 - 09:39] First, we'll import the layout component from EndDesign and the Home/Hero component from the Adjacent components folder. And we'll destructure the content sub-component from layout.

    [09:40 - 10:00] We'll have our Home/Components return statement return the content sub- component with the Home/Hero as a child. If we take a look at our homepage right now, we'll see the title, search input and the four different cards in our hero be shown.

    [10:01 - 10:12] Amazing. And if we were now to click one of these cards, we'll be redirected to the listings page or the listings route with the appropriate URL parameter.

    [10:13 - 10:23] Awesome. Now, as a note, there has been some CSS work, though not really difficult CSS work, to have discard/cover images here encompassed the entire card.

    [10:24 - 10:38] The card component from EndDesign doesn't really appear this way, so we've customized a few styles to get to this point. Next, do take a look at the root style index.css file we have in our project if you're interested in seeing how this might have been done.

    [10:39 - 10:55] Let's now do a few small changes to this hero before we move to the other sections in this homepage. We have a nice dotted map of the world available as an asset, and we'll look to make it the background image of this parent content component of Home.

    [10:56 - 11:25] So, we'll import this map background in the Home components file, and we'll place it as the background image of content. And we'll now get a nice background image at the hero level. Great.

    [11:26 - 11:44] At this moment, our search input here doesn't do anything when we press the enter key or even click the button available to us. When a user searches for listings or searches for a location, essentially, we 'll want to direct them to the listings page with the search that they've made as part of the URL parameter.

    [11:45 - 12:11] So, for example, if the user searches San Francisco, we'll want them directed to the listings route where San Francisco is the URL parameter. We'll create a function in the Home component called onSearch that'll handle this functionality to make a redirect to the new route, and we'll pass this function down as a prop to the HomeHero component.

    [12:12 - 12:23] How would this onSearch function behave? The Home component here is a component that's rendered as part of a route within the context of our React Router routes and components.

    [12:24 - 13:09] Any component that's rendered directly as the component of a route has access automatically to an object as a prop known as the History object, which is a reference to the browser's session history. We can use this history object to push a new entry to the history stack, or in other words, directly user to a new location. So, let's see how this can work. So, first, if the history object is available as a prop, we should be able to access it directly, but we don't know what its type is, which is why we can use the Route component props interface from React Router DOM to help declare the shape of props a component receives when rendered as a route.

    [13:10 - 13:37] Here's where we can use the HistoryPush method to take the user to a certain route. The user is to provide what location they'll want to go to and see listings for . So, we can get this information as a parameter of this function that will label as a value and will be a string, and we'll say History.PushListings and we'll append whatever the value is.

    [13:38 - 14:02] With our function prepared, we'll have to now declare it in the HomeHero component for the search input to use. So, in the HomeHero component, we'll state that it is to accept a function prop called OnSearch, that is to accept a string argument and return Void, or that is to say nothing. We'll declare the OnSearch function prop and pass it to the search input to use .

    [14:03 - 14:25] And now, let's see how this would behave. If we typed something in the Input and pressed Enter, or clicked the Search button will be redirected to the List ings route with the appropriate URL parameter applied. Fantastic. Now, this input will behave mostly as we would want.

    [14:26 - 14:48] We're not going to spend too much time trying to come up with the most appropriate field-level validations or anything. If the user is to type something completely incorrect, like a random set of strings or numbers, what we'll do instead is we'll direct the user to the List ings page, and then the query will eventually create, would query and fail because we're not providing suitable information.

    [14:49 - 15:09] However, the one thing we will handle is an attempt to remove any white space characters from the beginning and end of a string. So, for example, if a user is to type something like this, where there's a large number of white space characters in the beginning and the end, we wouldn't want these white space characters to be part of our route.

    [15:10 - 15:21] However, if there is white space characters in the middle, we'll leave it as is , but we'll remove the ones from the beginning and end. Thankfully, there's actually a JavaScript function that does just a disk.

    [15:22 - 15:35] It removes white space characters from the beginning and end, and is adoptable in almost all browsers, and is called the trim function. So, we'll create a constant value in our function called trimmed value.

    [15:36 - 15:56] That is the trimmed string, and we'll pass that as the URL parameter for the Listings route. And now, if we were, as an example, to type a string with white space characters in the beginning and we pressed Enter or click the Search button, we'll have them removed from our URL parameter.

    [15:57 - 16:11] The one other condition we'll guard for is if the user doesn't type anything at all but just empty spaces. If the user was to just type empty spaces only, the trim function will trim all those spaces away for it to be an empty string.

    [16:12 - 16:26] So, we can place an if statement in our function and say, if the string is not empty, then take them to the Listings page. If the string is empty, we'll display an error that perhaps says, please enter a valid search.

    [16:27 - 16:50] And we'll use the display error method utility function we've set up before to help us display this error. [silence] And now, if we were to just type empty characters in an attempt to make a search, it'll throw the visible error message.

    [16:51 - 17:03] Awesome. [silence] Great. And this is to be pretty much our home hero.

    [17:04 - 17:23] The rest of the home page, without considering the home listings, is going to be pretty straightforward, so we'll look to build it out fairly quickly. The next section of our hero is going to have a title and description with some copy and it'll be a call to action that when the user is to click it, it'll take them to the popular listings in the United States.

    [17:24 - 17:40] We're going to use the dialog here, popular listings, but we're just simply going to take them to the listings within the United States. In this case, however, the location isn't a city, but an entire country, and Google's Geocode API will help us achieve this as well later on.

    [17:41 - 18:10] So, let's create this section. First, we'll import the typography component from AntDesign, and we'll dest ructure the title and paragraph sub-components from typography, and we'll have a new div element that has a title that says "Your Guide for All Things Rental", and there'll be a paragraph that says "Helping you make your best decisions in renting your last minute's locations".

    [18:11 - 18:44] And we're interested in providing a call to action that appears as a button from AntDesign, but behaves as a link within the context of React Router. There's a few ways we can try to achieve something like this, by perhaps wrapping the AntDesign button over React Router's link component, or vice versa .

    [18:45 - 19:08] We can even create our own button link component that will use the history object to push a new entry into the browser session. However, the simplest way we can sort of do this right now, since AntDesign's button doesn't really act as a link, is we can simply use the link component, which is to become an anchor tag when rendered, and we can provide the classes that constitute how this link will look like.

    [19:09 - 19:25] So, we can achieve this by using the following classes. AntButton to have it appear as an AntDesign button, AntButton primary for it to appear as the primary blue color, and AntButton large for it to have a larger size.

    [19:26 - 19:36] And we'll pass a class we've created before that adds a little margin to the button. And finally, we'll import the link component from React Router.

    [19:37 - 19:57] Now, keep in mind, when we use these components from UI frameworks like Ant Design, at the end of the day, they render normal HTML markup with certain styles and classes associated with them. In this case, we're simply just using certain classes we want to get the button shape for the link we'll want to show.

    [19:58 - 20:25] And we'll specify a target path for this link to be "Listing's United States" provide some text in the link itself to say "popular listings in the United States". If we looked at our page now, we'll see the new section be shown, and if we were to click the call to action, we're taken to the listings route with United States as the URL parameter.

    [20:26 - 20:39] Awesome! Another section we'll add will be a simple two-column split that shows a nice image with some nice content or copy for one of two cities, San Francisco, US, and Cancoon, Mexico.

    [20:40 - 21:03] Each of these images will be linked as well to take us to the listings page for each city, and these images are available to us as some of the assets we have. So, first and foremost, we'll import two additional AntDesign components, column, and row, and we'll import the San Francisco and Cancoon images from the local assets folder.

    [21:04 - 21:43] We'll build the next section that I have a title of "Listings of Any Kind". There'll be a row where each column has full width and extra small viewports and half width of the grid in small viewports and graders, and for each of these columns, there'll be a link with a div and an image.

    [21:44 - 22:26] The first link will take us to listings San Francisco, and the second link will take us to Cancoon. And for Cancoon, we'll be sure to use the Spanish "U" character, though the query will eventually set up, we'll be able to determine we're searching for C ancoon even if we weren't using this character.

    [22:27 - 22:44] And if we took a look at our page right now and scroll to the bottom, we'll see the two separate images shown to us. If we click the one on the left, we'll be directed to listing San Francisco, and if we were to click the image on the right, we'll be navigated to listings Cancoon.

    [22:45 - 22:49] Fantastic. Our whole page is now looking pretty good.

    [22:50 - 22:56] We're able to use the search to go to the listings page. Each of the images we show were also links to take us to the listings page.

    [22:57 - 23:08] Now, in the next coming lesson, we'll display or show the premium listings, and we'll make that query to get that information. Great job so far.