Building a News Site - Home Page
Create the home page for NEWSLY, a shadcn/ui news site
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo 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 Sleek Next.JS Applications with shadcn/ui course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Sleek Next.JS Applications with shadcn/ui, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

[00:00 - 00:08] In the previous lesson you will fetch articles from the news API into the app. In this lesson you will render those articles and create the homepage of the site.
[00:09 - 00:23] You will use the image component from next year's the AspectRashio component from shadcn and the typography components you've already implemented. You're also going to compose new components based on all of those.
[00:24 - 00:29] The first component you'll build is the headline component. You can see it on the screen.
[00:30 - 00:38] So create a new file in component/topheadline.tsx. Headline.
[00:39 - 00:58] Now import the next JS image component, the article type we created on the previous lesson, and the H1 and lead components. Perfect.
[00:59 - 01:24] Now let's create the component itself. Export const, topheadline, equal a function that receives article as a prop from type article, then render a wrapping div.
[01:25 - 02:00] Apply to the div class name with a grid layout, width full, grid rows 1 items-start, gap 4, empty, md:grid-cols, and then we're going to split the two columns into 35 and 65%, and an empty gap, 8. So on mobile you're going to split the grid to 35 and 65, a fraction of the page for the image on one side and the text on the other.
[02:01 - 02:26] On mobile each element inside the grid will be a row thanks to the grid rows 1 class name. Now render a wrapping div for the image and apply a relative positioning to it with full and on larger screens we want to apply the H full height full class name.
[02:27 - 02:50] Render an image from next JS, the source should be article URL to image, we want to give it an alt, we want to apply a field property to it, and we want the class name to be object cover and make it rounded on larger screens on desktop basically. Perfect.
[02:51 - 03:06] Now let's render the text itself. We've applied some padding, we made it a column layout and we apply the flex.
[03:07 - 03:23] This is the display, then we're going to render the H1 because it's the top headline with the article title inside, a span, but we want to define it as a block. We want to make the text small, medium and orange.
[03:24 - 03:37] And on larger screens we want the text to be the default size. Then we want to render the author and then the publishedAt date.
[03:38 - 03:45] And we want to apply a lead that will show the article description. So this is the component, this is what we got.
[03:46 - 04:03] Now go to app/page.tsx and replace the hello world card with the top headline component and don't forget to pass an article to it. Which is the article at the first index of the array.
[04:04 - 04:12] Now let's go back to localhost 3000, don't forget to run the server. And we'll probably see this error, error invalid source prop.
[04:13 - 04:25] So the image component in next.js is required to define the domains from which images will be loaded. However, since you're using an external API, then use API.org one, you don't know all the domains that will be used for the images.
[04:26 - 04:36] The solution is adding a pattern for any HTTPS site. Go to next.config.mjs and add the following lines inside the next config object .
[04:37 - 05:02] Images which receives an object, not an array, with a remote patterns array. And you can provide it a list of your real patterns, which we want to apply and asterisk to, because we want all HTTPS sites to be loaded inside our applications.
[05:03 - 05:11] Now go back to the site, don't forget to restart the server. And refresh the page.
[05:12 - 05:19] You should now see the top headline component. If you don't see, try restart the server again or reach out for it.
[05:20 - 05:26] Now the article date is formatted incorrectly. Let's start by fixing this.
[05:27 - 05:56] We want to install the "dayjs” library import the "js" inside the top headline component. Now wrap the article published with dayjs and call the format function within hour and minutes format.
[05:57 - 06:00] Go back to the page. Don't forget to refresh.
[06:01 - 06:07] And you can see that now the date is formatted correctly. Now we want to fix the image.
[06:08 - 06:14] You can see that the image is rendered badly. We want it to catch all of its width in the correct aspect ratio.
[06:15 - 06:29] Now the aspect ratio of an element describes the proportional relationship between its width and its height. shadcn/ui provides us with aspect-ratio component that we can just install and use so the images are rendered correctly.
[06:30 - 06:42] So in your favorite terminal, run npx shadcn/ui@latest add aspect-ratio. Now Restart the server.
[06:43 - 06:59] And then let's create a reusable component called "article image". Import the aspect-trashy component you've just installed and the image component from "next.js".
[07:00 - 07:14] Perfect, let's create the component. The component receives two props, which are the title, source, and ratio, sorry, three props.
[07:15 - 07:22] Title is a string, source is a string as well. And ratio is either "tall" or "wide".
[07:23 - 07:34] I'll explain it in a second. Now let's render the aspect-trashy component and apply a ratio to it as well.
[07:35 - 07:46] If the ratio is "tall", we are going to pass a "4" divided by "3" ratio. And if it's "wide", we're going to divide a "16" divided by "9".
[07:47 - 08:06] We're going to pass a "16" divided by "9". Now render the image, apply the source prop to it, the title, to the "outprop", a "fill", and the same class names for "before", object cover, and md:rounded.
[08:07 - 08:12] Don't forget to close your image. And don't forget to add the "use client" at the top of the component.
[08:13 - 08:31] The aspect-trashy component uses the "use effect" to "from react", which is not available on the server. Now import the "artical" image component on the top headline component and replace the image rendering with the "artical" image component.
[08:32 - 08:45] Don't forget to pass it to "title", a "source", and a ratio of tall. A "source", your relative image.
[08:46 - 08:49] Perfect. Don't forget to close it as well.
[08:50 - 08:59] Now the server will restart and we should see the image rendered correctly. Great.
[09:00 - 09:14] Now you can see that the element of the top headline is not receiving a full width. So let's go back to the page and we can remove the max width extra, the max width-xl.
[09:15 - 09:18] Great. Now let's style the page layout.
[09:19 - 09:28] Right now the spacing between your top headline component and the inner container of the page is not quite as we want it to be. We can look at the side design again.
[09:29 - 09:58] And we want it to be with the vertical padding and an horizontal padding as well. So go to "app/layout_tsx" and wrap the children component with the wrapping container that will add some spacing under "reflex1" element and another div.
[09:59 - 10:08] And apply class name to it of "mxauto" which will provide an auto margin for the margin left and margin right properties. Agreed.
[10:09 - 10:27] A gap 16 and a padding vertical of 16 for large screen and max width 6 excel for large screens. Let's look at it again.
[10:28 - 10:59] Starting to look better. The next step will be to add the bottom latest story section.
[11:00 - 11:10] And we want to update the container div to be "flex" "flex-cols" and "gap 16". Now under it add a new class name.
[11:11 - 11:15] Make it class grid. Add a "8" gap.
[11:16 - 11:29] And for larger screens we're going to make it a custom grid. Sorry a grid-cols 0.3fr and 0.7fr.
[11:30 - 11:50] So we're going to have the layout of the stories catching 30% of the screen width and the other stories catch the rest which is the 70%. So go back to page.tsx and make sure you've added the layout.
[11:51 - 11:56] Now looking closely at the design you may notice a reusable component between the latest stories. The article details.
[11:57 - 12:08] There is an author, a title and a description. So now we want to create the article details component which we're going to use for both.
[12:09 - 12:17] Go to components and create a new file called article details. Import the "dayjs" component.
[12:18 - 12:22] Import the "dayjs" from the "dayjs". Import the article type.
[12:23 - 12:44] We created on the previous lesson. Then import the "large", " muted" and "small" typography components.
[12:45 - 13:03] Now create a component. Export "const" article details equal a function that receives an article as a prop like most of the other components and return a div with a flex-col layout.
[13:04 - 13:16] gap-2, add add a minor border at the bottom. Add a minor padding at the bottom. And for the last item on the list, the last of type, we don't want to render the border.
[13:17 - 13:25] So we want to modify that. Now apply the small render, the small component.
[13:26 - 13:39] This is for the author and the dates. So article, author or no author if we don't have one.
[13:40 - 13:45] And then add a divider. We want to add a space here as well.
[13:46 - 13:54] And render the article publishedAt as we did before. With a format for the hour and the minutes.
[13:55 - 13:59] Now render a large component. Add a font bold.
[14:00 - 14:11] Class to it. And a hover, we want to make text orange And we want to take the text larger on medium and larger screens.
[14:12 - 14:20] And render the title. Now the last part is render the muted component.
[14:21 - 14:27] And we don't want to render it on small screen, only on larger ones. And render the description.
[14:28 - 14:32] Great. Now you may notice the errors on the typography components.
[14:33 - 14:38] That's because we haven't added the class name to them. Like we did for the H1 for example.
[14:39 - 14:56] So you know the drill already, add a class name optional class name prop to the components. And change the class name to be a call to the cn function that receives a default class name, the one we have now, and the proper last name.
[14:57 - 15:04] So let's edit. Okay.
[15:05 - 16:18] - Added to the lead in the H2 components as well, because we'll need it later. (silence) Perfect! Now we can compose the latest article component from the article details alone.
[16:19 - 18:02] Import article from types article, import the article details component we've just created, and import the h2 component. Export columns, latest articles, equal a function that receives articles as a prop, which is an array of articles, and return a div with a flex call layout as we say, add in height full class as well, add a minor gap and a minor padding, and limit the size and the width of it on larger screens. Now we want to render a title, which is h2 to component, and we want to apply a special class name to it, so no border, we want to make it for Excel on the text size, we want to make it bold, upper case, and we want to make the text orange, and we want to make a text orange 700 as we need for a most of header’s primary text and we want to call it latest stories and we want to map the articles and return a detailed component.
[18:03 - 18:30] Great, you can see that the app is now compiled, and you want to add the latest articles component to page GS6. Pass articles to it, we're going to slice between the one and the fourth component, the articles, and let's look at the page again.
[18:31 - 18:46] Perfect, the articles are rendered correctly. The last step will be to add the other stories to the page. So now create the article preview component.
[18:47 - 19:04] Article preview is a composition of the article details component, and the article image component that we've just created. If you look at the designs, we have the article image here, and we have the article details on the sides of it.
[19:05 - 19:36] So go back to the article preview component, import the article type, import the article details, and import the article image components. Now create the article preview, which receives only a single article.
[19:37 - 20:09] Wrap both the components with a flex, gap-4 class name, add a wrapping component to the image with a relative, width, full, and rounded class names. If the article has a URL because sometimes they don't render the article image component, render the title, and don't forget to provide an alt as well.
[20:10 - 20:32] If we don't have one, we simply want to render an image. Now create a wrapping div for the article details component, component, and limit the size of it. We also want to pass the ratio of width, and render the article details component and pass it an article.
[20:33 - 20:48] We're going to learn more about composition in the eighth module. We can remove the alt component, it was my mistake. Now add the article preview component in app/page.tsx.
[20:49 - 21:10] Now add the article preview component to app/page.tsx. Go over the articles, first wrap it with a div. We want to make them render in a column layout, and add this a bit of padding.
[21:11 - 21:29] And then call the article preview component. Don't forget to pass the key again . Sorry, first loop through the articles. We want to do between the fifth and the eighth.
[21:30 - 21:50] And now we should return the article preview with the key of the title, and the article itself. Let's refresh the page.
[21:51 - 22:10] And we can see that the component is rendered, but the layout is incorrect, so let's check why. That's because the component is outside our layout. It's from our page. Refresh again. And now the page looks exactly the same as it looks in the designs.
[22:11 - 22:18] In the next lesson, you're going to render different articles based on the selected category of the user.