Now that we worked on the starting page
and the community page and improved the header,
it's finally time to work on that meals page
and it's time to output some meals there
before we then thereafter,
will make sure that users can also share meals.
Now for that, it's this page.js file
in the meals folder on which we'll work.
And there, I wanna output a bunch of meals,
a bunch of meals
which we'll soon store in a database.
But before we'll do that,
let's set up the base markup of this page,
the base structure of this page, you could say.
And here I again wanna have a header section,
and then below that, the main section of this page.
I also again prepared some styles
and therefore, attached,
you'll find another page.module.css file,
the page.module.css file for this meals page.
And we can and should then import this page.module.css file,
again using this special CSS modules import syntax
so that we can access the CSS classes set up in that file.
Because there, for example,
should be a class added to this header,
the header class like this,
I also wanna add a class to the main section,
and that is the main class that's added here.
Now inside of that header, I then wanna have an h1 element
where I say, delicious meals, created,
and then a span, by you.
And I'm using that span for some extra styling
because to that span, I wanna assign a class.
To be precise, the highlight class like this.
Below this h1 element, we can then add a paragraph
which simply outputs some dummy text,
like choose your favorite recipe
and cook it yourself.
It is easy and fun.
You can of course output whatever you want.
Now below that paragraph, I wanna have yet another paragraph
which should receive a className of cta
for call to action, because in there,
I then wanna have a link using the next/link component,
which therefore, of course,
also is imported as it always is.
And I'm using that Link component
to link to that share page,
so to /meals/share,
which will be the page that can be used by users later
to share their own meals with the community.
And therefore, I'll say, share your favorite recipe.
So that's now this header area of this MealsPage.
And we could of course put that into a separate component.
If you wanted to, you could do that.
You just have to make sure
that you bring along the necessary styles.
But here, I'll keep it all in one component.
I will add a separate component
for outputting the meals though,
and therefore, for that, back in my root components folder,
I'll add a meals subfolder
to store any meal-related components.
And there, I'll add a meals-grid.js file.
Now the job of this meals-grid.js file
will be to output a bunch of meal items in a grid.
So therefore, of course,
we start by exporting a component function.
I'll name it MealsGrid.
And I expect to get the meals
that should be output here as a prop.
And then I'll output an unordered list.
And in that list, I'll map through all meals
so that for every meal, we can output a list item.
Now every list item here needs a key,
and the key can be meal.id
because every meal will have an id.
And then inside of that list item,
I wanna output the meal item details.
Now before we work on that,
I also prepared a CSS file, meals-grid.module,
which you'll find attached,
which you should then import into this meals-grid.js file
just as we always did it with all those .module.css files
so that we can add a class here to the unordered list,
a class called meals like this.
And now I'll add another component here,
the meal-item.js file.
And I prepared a meal-item.module.css file,
which is also attached.
And attached you'll also find my finished meal-item.js file,
which is a relatively straightforward component
that simply accepts a bunch of props
and then outputs those props in a structured way.
Now what's interesting about this component
is, for one, that it then has a link
that takes us to a specific meal detail page.
And here, the path is constructed dynamically
with a dynamically injected segment
that will be different for every meal
because we have that dynamic meal details page.
Here, this page, which expects a dynamic value
for that path segment so that we can can visit
that details page for different meals.
And in the end here where I'm constructing this path,
it's this dynamic slug,
which will be different for every meal
that will be used as a concrete value for this placeholder.
So that's one interesting thing in this component
that will have this dynamic link
that will lead us to different meal detail pages.
The other interesting thing is this image component.
It's this next/image component.
And it's interesting here
because the images I'm outputting here
will now not be imported manually from the assets folder.
Instead, we'll be working with meals
that are stored in a database
and their images are stored in the public folder.
Now it turns out that these are the same images
as in the assets folder,
but that's just in coincidence here.
Later, users will be able to upload their own images
and share their own meals,
and that's something we have to keep in mind
when using this image component
because this image component
needs to know the underlying width and height of the image
that's being output.
So not the width and height you wanna have on the screen,
but the width and height
of the image you are trying to render.
And it's able to detect that automatically
when you are using the image component
as we did it in the header on an image
that's imported from the local filesystem
as we're doing it here with the logo.
In that case,
NextJS is able to look up the width and height.
But for those meal items,
we'll load them dynamically from a database.
And then in the database,
we'll have a path pointing to some image,
and NextJS will not be able to resolve
the width and height of such an image,
of such a dynamically loaded and resolved image
simply because the information
is not available at build time,
as it's the case for all imported images,
but only at runtime.
And that's why I added this special fill prop here.
An alternative would've been to explicitly set
the width and height of the images that will be output here,
but the problem is that I don't know that in advance
because we'll also be dealing
with images shared by the user.
And in such scenarios, fill is an alternative.
This then tells NextJS
that it should simply fill the available space
with that image as defined by its parent components.
And I set up appropriate CSS styles
that make sure that this will look good.
And I'm mentioning this here
because it is of course a common scenario
that you're displaying images
where you might not know their dimensions in advance.
And in such cases, fill is a great alternative
to explicitly setting a width or height,
which you should do though
if you know the width and height in advance.
So that's therefore this MealItem component added.
And with that added,
we can output it here in the MealsGrid component.
So here between those li tags,
I'll output the the MealItem like this.
And I'll simply forward all those meal properties
from that meal we have here to that MealItem.
So I use this syntax
to pull out all the properties of that meal object
and spread them as key-value pairs,
so as props in the end, onto this MealItem.
And since the meal I get here,
since the meals I'll have in this meals array eventually
will have all those properties
that are expected as props here,
this approach will work just fine.
Now, of course, at the moment we have no data source yet,
so at the moment, we won't see anything on the screen.
Nonetheless, we can already take
this MealsGrid component here
and go to this page.js file in the meals folder
and output it here.
But at the moment, I'll set meals to an empty array
because, as mentioned, we have no meals yet.
So if we save that and we go to the Browse Meals page,
we see our header,
which allows us to go to that share meal page,
but we see no meals yet,
but that's what we'll add next.