
Join the Conversation!
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
"Please login to view comments"
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
Metadata is like a little summary about your page. Search engines read it first to figure out what your page is about.
If your website was a product, metadata would be the label on the box. 📦
A clear, helpful label means more people are likely to check it out.
There are two ways to add Metadata to your Next.js applications.
With this, you can add special metadata by simply dropping certain files in your /app directory:
Just include them at the root of your app directory, and Next.js will auto-detect them and generate the right meta tags.
⚠️ Note: File-based metadata has higher priority than config-based metadata, so be mindful if both exist.
You can learn about all types of special files you can use to define file-based metadata here.
This is where you use JavaScript inside your page or layout files to define metadata. There are two types:
If your page content doesn’t change based on route params (like a homepage or About page), you can just export a simple metadata object in the root layout
export const metadata: Metadata = {
title: "Dev Overflow",
description:
"A community-driven platform for asking and answering programming questions. Get help, share knowledge, and collaborate with developers from around the world. Explore topics in web development, mobile app development, algorithms, data structures, and more.",
icons: {
icon: "/images/site-logo.svg",
},
};
Next.js automatically turns that into:
<title>Dev Overflow</title>
<meta name="description" content="A community-driven platform for asking and answering programming questions. Get help, share knowledge, and collaborate with developers from around the world. Explore topics in web development, mobile app development, algorithms, data structures, and more.">
<link rel="icon" href="/images/site-logo.svg">
You don’t need to manually write tags inside the <head>—Next.js takes care of that for you.
Also, when you define metadata in the root layout file, it automatically applies to all pages nested under that layout.
But if you want to customize metadata for a specific page—say, the homepage—you can simply define a different metadata object right inside that page file, like this:
export const metadata: Metadata = {
title: "Dev Overflow | Home",
description:
"Discover different programming questions and answers with recommendations from the community.",
};
This page-specific metadata will override the default metadata from the root layout, giving you full control over what shows up in the browser and search engines.
Pretty cool, right?
When you have dynamically generated pages—like blog posts, product details, or question pages—each one needs unique metadata so search engines can index them accurately.
For example, if a page is about a specific monitor, the page title should mention that monitor. Or, if it's a question page about how to integrate a database in NestJS, the title should reflect that specific topic.
This is where generateMetadata() comes in. It allows you to generate metadata dynamically based on the content of the page.
To show it in action, go to app/(root)/questions/[id]/page.tsx page and do this before the component definition:
export async function generateMetadata({
params,
}: RouteParams): Promise<Metadata> {
const { id } = await params;
const { success, data: question } = await getQuestion({ questionId: id });
if (!success || !question) return {};
return {
title: question.title,
description: question.content.slice(0, 100)
};
}
Go and test it by opening different questions now. Your page title should reflect whatever the question title is.
You might be wondering, "Aren’t we making two requests on the same page? Doesn’t that hurt performance?"
The good news is—not at all if you’re using fetch requests 😄
Next.js automatically memoizes (caches) fetch calls across layouts, pages, and server components when they request the same data. So even if you call the same function in both generateMetadata() and your page component, it won’t run twice.
However, if you're using server actions, you'll need to handle memoization yourself.
To do that, you can use React's built-in cache() function.
Go to lib/actions/question.action.ts and wrap your getQuestion function with cache() like this:
import { cache } from "react";
export const getQuestion = cache(async function getQuestion(
params: GetQuestionParams
): Promise<ActionResponse<Question>> {
const validationResult = await action({
params,
schema: GetQuestionSchema,
});
if (validationResult instanceof Error) {
return handleError(validationResult) as ErrorResponse;
}
const { questionId } = validationResult.params!;
try {
const question = await Question.findById(questionId)
.populate("tags", "_id name")
.populate("author", "_id name image");
if (!question) throw new Error("Question not found");
return { success: true, data: JSON.parse(JSON.stringify(question)) };
} catch (error) {
return handleError(error) as ErrorResponse;
}
});
By wrapping it like this, even if getQuestion() is used in both generateMetadata and the page component, the data will only be fetched once. Clean and efficient.
Whenever a user (or crawler) visits a dynamic route like /questions/question_title, Next.js generates the correct metadata for that page on the fly. This helps each page rank individually in search results.
Basically, if you’re dealing with things like favicons, Open Graph images, or other assets that stay the same across your site—use file-based metadata. It’s the best fit for those constant elements.
When it comes to page-specific metadata, use config-based metadata:
In short, use the right kind of metadata in the right places to give your whole site proper SEO and structure.
What are you waiting for? Go give your site that SEO glow-up 🌟
Complete source code for this lesson is available at
Generate Metadata
React Cache
00:00:00 While you were implementing dynamic metadata, you might have asked yourself, But hey, Adrian, are we not doubling the getQuestion request right here?
00:00:09 We're calling the getQuestion to first get all the question details and then show them on the screen, and then we're doing it one more time to get the
00:00:17 question details to show it within the dynamic metadata.
00:00:20 Aren't those two requests, and therefore making our page that much more unoptimized?
00:00:25 Well, if you're saying that, you'd be right.
00:00:28 The reason for that is that the server action calls are not getting automatically cached.
00:00:34 If we were using fetch functionality right here to just get access to our question details like this, in that case, Next.js would automatically cache that
00:00:44 request and we would be good.
00:00:46 But considering that we're using server actions, that automatic caching is not happening.
00:00:51 So, what do we do?
00:00:52 Thankfully, there's a solution to that problem.
00:00:55 React cache.
00:00:56 Specifically, for React server components, it lets you cache the result of data fetch or computation.
00:01:03 It's super simple as well.
00:01:05 You get a cached version of the function simply by wrapping the original function in a cache call.
00:01:11 So, how do we do it?
00:01:12 Well, let's head over to where this question had been asked.
00:01:15 It is within question action.
00:01:17 And instead of simply exporting this asynchronous function, we can say const getQuestion is equal to a cache version of that function.
00:01:29 So we can simply wrap this function, async function getQuestion, within this cache call.
00:01:37 So let's properly close it.
00:01:40 And don't forget to import cache from React.
00:01:43 And therefore, we can then export the new cached version of the getQuestion function.
00:01:49 Nothing changes here, nothing changes here, but now only one request is going to be made instead of two.
00:01:55 This is how you can memorize functions in Next.js with React cache.
00:01:59 Perfect.
00:02:00 So if you're not using the basic fetch API, but rather have created your own versions of server actions like we have for the entire application,
00:02:08 now you know how you can cache those too.
00:02:11 So your app doesn't make any extra additional requests that it doesn't have to.
00:02:16 Great work.
00:00:00 In this video, I'll show you how to implement dynamic metadata using Next.js.
00:00:05 Sure, there are different ways of adding metadata, such as static metadata object or a dynamic generateMetadata function within each layout or page.js.
00:00:16 Static metadata is super simple.
00:00:18 You just add it, define the title and description.
00:00:21 But in this case, we want to go a bit further and I want to teach you how you can dynamically generate metadata for every single page within your application.
00:00:31 What do you think?
00:00:32 For which page would it make the most sense to implement the dynamic metadata for?
00:00:37 Homepage?
00:00:38 Well, not really.
00:00:39 Homepage of Devflow will always be the homepage of Devflow, so we can leave that in the title.
00:00:44 Where dynamic metadata makes sense is within the content that changes, or the content that is new and generated, like blog posts.
00:00:52 For example, let's say that we go into the how does the data renderer component work.
00:00:57 And here, you want to dynamically take this title, potentially the description, and hey, maybe even a thumbnail if it had one,
00:01:05 and then display it within your browser.
00:01:07 If I show you the tab right here, even though it's not as obvious as it is in Google Chrome, for example, you can still see that the title of the application
00:01:16 is just Devflow.
00:01:17 It doesn't really say on which page or question we're on.
00:01:21 So let's implement the dynamic metadata for the question details page.
00:01:26 We can do that by heading over into questions ID page.
00:01:31 So that is the question details page right here, questions ID page.
00:01:36 And right at the top, above the function or above the component, we'll implement this new function.
00:01:42 Export async function, generate metadata.
00:01:49 And it'll take in the params as its first and only parameter.
00:01:53 And it'll be of a type route params.
00:01:56 And it'll return a promise that ultimately gets resolved in the metadata that we need.
00:02:02 That's how it looks like.
00:02:03 So now within it, let's get access to whatever we have in the params.
00:02:08 That's going to be ID.
00:02:10 Because for this page, we get the ID of the question we're currently on.
00:02:15 Then here's the cool part.
00:02:16 Before you fetch the page, you can also get access to the details of that thing that you're trying to show.
00:02:23 So similar to how we're trying to get the question here, I'll also make that same request here.
00:02:29 Cons success and data, which is the question.
00:02:32 is equal to a wait get question with that specific ID.
00:02:36 Once we do that, we can do some if statements and say if there's no success or there's no question, we just want to return,
00:02:44 well, we can do a title and description, question not found.
00:02:49 But else, we'll return a metadata object.
00:02:53 So I'll give it a title of question.title.
00:02:57 I'll give it a description of question.content, and I'll slice it to the first 100 characters.
00:03:05 I'll also take in and create a Twitter card by saying that the card will be of a type summary, large image.
00:03:14 And I'll give it a title of question title and a description of question content slice 100. If you had an image, you would be able to put the URL of that
00:03:24 image here as well, making it even better.
00:03:27 Now, if you save this and open up the tab, you'll see that now the tab actually says, how does the data render component work?
00:03:35 Because we're on that specific question.
00:03:38 And if you switch over to another question on our list, maybe we go with this one.
00:03:43 How does the Node.js event loop work?
00:03:45 Well, if you take a look at the top title, you can see it.
00:03:48 And that's the benefit of the dynamic metadata.
00:03:51 But it's not only that.
00:03:53 There's this website called OpenGraph.
00:03:55 If you go to OpenGraph and type in a URL of a specific website, for example, jsmassread.pro, and you check it, it'll actually tell you what kind of OpenGraph
00:04:06 data does your site have.
00:04:07 And this is typically referring to the metadata.
00:04:10 So you can see that I am utilizing the full powers of this dynamic metadata on my website.
00:04:16 where the title is called your way to a life changing career with a full description and even an image.
00:04:22 So when you share it on Discord, LinkedIn, X, Facebook, it looks like this.
00:04:27 But if I had to do something like membership, you'll see that it'll have a completely different thumbnail.
00:04:35 And the same thing will happen if we go to a dynamic page, like the ultimate Next.js 15 course, which has its own title,
00:04:41 description, and thumbnail.
00:04:43 And with what we have done right now to our Devflow, we achieve the same functionalities.
00:04:48 So now if you want to learn how to fully optimize the SEO and searchability and shareability of your website on different platforms like Twitter,
00:04:58 LinkedIn, or in general on the internet, always make sure that your metadata properties make sense.
00:05:03 So I'll go ahead and commit this by saying implement dynamic metadata.
00:05:09 Of course, we have done it for just a single file here, but you can go ahead and do it for other files as well.
00:05:15 Perfect.
00:05:16 Now we know how to implement dynamic metadata within any Next.js application.