In a Next.js App Router project, configure page metadata in a server-rendered layout or page, keep secrets in server-side environment variables, and choose one caching model before setting lifetimes or invalidation. Cache Components, introduced in Next.js 16, uses opt-in cache APIs; the previous App Router model uses fetch and route-segment controls. The examples below keep those models separate.
How do I add metadata in Next.js?
Use a metadata export when values are known in advance, and generateMetadata when they depend on route parameters, fetched data, or parent metadata. Both are supported in Server Components only, and a single route segment must not export both.
As an Amazon Associate I earn from qualifying purchases.
Set site-wide defaults in the root layout
A root layout is a useful place for defaults and metadataBase, which resolves relative URL-valued metadata such as canonical or Open Graph image URLs. Without metadataBase, a relative URL value can cause a build error. Absolute URLs do not need it.
import type { Metadata } from 'next'
export const metadata: Metadata = {
metadataBase: new URL('https://www.example.com'),
title: {
default: 'Example site',
template: '%s | Example site',
},
description: 'Guides and updates from Example site.',
}
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
Replace the example hostname and copy with your site’s real values. A child page can add route-specific metadata without repeating the site-wide defaults:
#1 Best Overall
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Installation guide',
description: 'Install and configure the application.',
}
export default function Page() {
return <main>...</main>
}
Next.js resolves metadata exports into the relevant document head tags. They are useful for describing a page and controlling how it is presented when shared, but the documentation does not establish a ranking guarantee for any individual metadata field.
Generate metadata from route or fetched data
Use generateMetadata when the title or description depends on the current route or a record loaded from a data source. For example, the page and metadata can call the same fetch function; Next.js documents memoization for matching fetch requests, and React cache for non-fetch data access.
import type { Metadata } from 'next'
type Props = {
params: Promise<{ slug: string }>
}
async function getArticle(slug: string) {
const response = await fetch(`https://content.example.com/articles/${slug}`)
if (!response.ok) throw new Error('Could not load article')
return response.json() as Promise<{ title: string; summary: string }>
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params
const article = await getArticle(slug)
return {
title: article.title,
description: article.summary,
}
}
export default async function Page({ params }: Props) {
const { slug } = await params
const article = await getArticle(slug)
return <main><h1>{article.title}</h1><p>{article.summary}</p></main>
}
The example uses the promise-based params form documented for current App Router pages. Match the prop shape to the Next.js version used by your project if its route parameters are typed differently.
Use metadata files for assets
Next.js also recognizes special metadata files for items such as favicons, manifests, and Open Graph images. Prefer those conventions for metadata assets where they fit. File-based metadata has higher priority than values returned by the Metadata API, so check for a matching file if a configured value does not appear as expected.
Rank #2
Know when metadata can stream
With dynamic metadata, Next.js can append metadata after the initial UI for bots that execute JavaScript. HTML-limited bots continue to receive blocking metadata in the document head. The framework identifies HTML-limited bots from the user agent; the htmlLimitedBots setting can override that behavior, but expanding the set of blocking bots can increase response time. Treat that as a specific compatibility choice, not a routine optimization.
How do I use environment variables in Next.js?
Next.js loads supported .env* files into process.env. Keep them in the project root, including when application source lives in a src directory. The key distinction is whether the variable is server-only or intentionally exposed to browser code.
Keep secrets server-side
Variables without the NEXT_PUBLIC_ prefix are not exposed to client-side JavaScript by this mechanism. Read them in server-side code, and do not commit files containing secrets. The create-next-app template ignores environment files, and the production checklist likewise advises keeping .env.* files out of version control.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →// .env.local — keep this file out of version control
DATABASE_URL="postgres://user:[email protected]/app"
SERVICE_TOKEN="secret-value"
// Server-side code
const databaseUrl = process.env.DATABASE_URL
if (!databaseUrl) throw new Error('DATABASE_URL is not configured')
Do not import server-only configuration into a Client Component. A variable named NEXT_PUBLIC_* is public by design and must never contain a password, token, private key, or other secret.
Rank #3
Use NEXT_PUBLIC_ only for values safe to publish
Next.js inlines NEXT_PUBLIC_ variables into client bundles during next build. For example:
// .env.production
NEXT_PUBLIC_ANALYTICS_ID="public-id"
// Client-side code may read this value
const analyticsId = process.env.NEXT_PUBLIC_ANALYTICS_ID
Because the value is included in the built JavaScript, changing the deployment environment after the build does not update that client-side value. Build a separate bundle when a public value must differ between environments.
Choose build-time or runtime configuration deliberately
Public prefixed values are build-time configuration. For server-side values, the self-hosting guide describes reading environment variables during dynamic rendering so one Docker image can be promoted across environments with different runtime settings. Use that approach when the server value should be supplied at deployment or runtime rather than embedded in a build. Confirm that the code path is dynamically rendered; a value captured during a build cannot be changed merely by changing the running container’s environment.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Environment variable troubleshooting
- A variable is undefined: Check spelling, confirm the file is at the project root, and restart the development server after editing environment files.
- A browser cannot read it: Only intentionally public values should use the
NEXT_PUBLIC_prefix. Adding that prefix exposes the value; it is not a fix for a secret. - A variable contains another variable: The environment guide supports
$VARIABLEexpansion in environment files. Escape a literal dollar sign as$. - A tool outside Next.js needs the same values: The official guide points to
@next/envandloadEnvConfigfor tools such as ORM configuration or test runners.
How do I cache and revalidate data in Next.js?
First check whether cacheComponents is enabled in next.config. These are different models: Cache Components opts selected code into caching with use cache, while the previous App Router model uses fetch options and route-segment settings. Do not mix their controls as if they were interchangeable defaults.
| Decision | Cache Components model | Previous App Router model |
|---|---|---|
| Configuration | Enable cacheComponents: true; this is the Next.js 16 feature. |
Leave Cache Components disabled and follow the previous-model guidance. |
| How caching is expressed | Opt a route, component, or function into caching with use cache. |
Set caching per fetch and, where needed, with route-segment controls. |
| Lifetime controls | Use cacheLife. |
Use next.revalidate for a fetch resource or the route’s revalidate setting. |
| Tagging and invalidation | Use cache-tag APIs, including cacheTag, for tag management and invalidation. |
Tag fetches with next.tags; use revalidateTag or revalidatePath for on-demand invalidation. |
| Existing route settings | When enabled, Cache Components APIs replace the previous route-segment settings. | Use the previous-model controls only while this model applies. |
Cache Components: opt in to caching
Enable the feature in configuration, then mark only the code that should be cached:
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig
import { cacheLife, cacheTag } from 'next/cache'
export async function getCatalog() {
'use cache'
cacheLife('hours')
cacheTag('catalog')
return loadCatalogFromDataSource()
}
The use cache directive marks the function’s result as cacheable; cacheLife configures its lifetime and cacheTag associates a tag for cache management. The named default profile documented for use cache specifies 5 minutes of client stale time and 15 minutes of server revalidation (Next.js documentation, 2026). Those numbers belong to that default profile, not to every cache or to code whose lifetime is customized with cacheLife.
With this model, dynamic fetching can remain available at runtime while selected work is cached. If metadata alone reads request-time or uncached data but the rest of a page can be prerendered, decide explicitly whether to cache that data where possible or defer rendering. Do not assume metadata access is an invisible, cost-free exception.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsPrevious model: configure fetch and route lifetimes
If Cache Components is not enabled, set the resource behavior explicitly in fetch options. This example belongs to the previous model:
const response = await fetch('https://content.example.com/catalog', {
next: {
revalidate: 3600,
tags: ['catalog'],
},
})
In the previous-model fetch API, next.revalidate is a maximum lifetime in seconds: false means cache indefinitely, 0 prevents caching, and a number sets an upper bound. Route-segment controls such as dynamic, fetchCache, and revalidate also belong to this model. The lowest relevant revalidation setting can increase how often a route is revalidated.
For on-demand invalidation in this model, a Server Action or other appropriate server-side handler can invalidate a path, for example with revalidatePath('/catalog') from next/cache. Use revalidateTag when invalidating data associated with a fetch tag. Be deliberate about which path or tag maps to the changed data.
Development behavior can differ from production caching. A refresh against the development server is not evidence that a production cache hit, lifetime, or invalidation behaves the same way; verify the behavior in the deployment mode you intend to run.
Recommended Free Tools
Self-hosted deployments need a cache architecture
The default self-hosted server cache is local to each instance’s filesystem. That can suit one persistent next start instance, but it does not automatically coordinate cache contents among several instances. If instances are ephemeral, requests can land on different machines, or a CDN or reverse proxy sits in front of the app, review shared cache storage, custom cache handlers, and invalidation coordination. A cache invalidated on one instance is not necessarily invalidated on another unless the deployment architecture shares or coordinates that state.
For a single persistent instance, the local default may be sufficient. For multi-instance or short-lived compute, decide how cache entries and revalidation signals are shared before relying on on-demand invalidation for consistent results.
Quick Recap
What should I check before deploying?
- Metadata is exported from a Server Component, with only one of
metadataorgenerateMetadatain each route segment. - Relative metadata URLs have an appropriate
metadataBase, and special metadata files are not unintentionally overriding API values. - Environment files stay out of version control; browser-visible values use
NEXT_PUBLIC_only when they are safe to publish. - Public environment values are set before building, while server runtime values are read during a dynamic rendering path when deployment-time variation is needed.
- The caching example matches the project’s selected model: Cache Components enabled, or previous-model fetch and route controls.
- For self-hosted multi-instance or ephemeral deployments, the cache storage and invalidation strategy matches how requests are distributed.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




