### What?
Removes the `experimental.devCacheControlNoCache` config option entirely
and hard-codes `no-cache, must-revalidate` as the dev server
`Cache-Control` header value.
Previously the option controlled whether the dev server responded with:
- `no-store, must-revalidate` (default, `false`)
- `no-cache, must-revalidate` (opt-in, `true`)
This PR first flips the default to `true`, then removes the option
altogether — making `no-cache, must-revalidate` unconditional in all dev
code paths.
### Why?
`no-cache` is strictly better than `no-store` for the dev server:
- `no-cache` allows the browser to revalidate (conditional
`If-None-Match`/`If-Modified-Since` requests), letting the server
respond with `304 Not Modified` when nothing changed → faster page loads
during development.
- `no-store` forces a full re-fetch every time, discarding valid cached
responses.
Since `no-cache` is the correct behavior for all dev users, the toggle
has no remaining value and can be removed to simplify the codebase.
### How?
**Two commits:**
1. `bcec825` — Flip the default from `false` → `true`; update
tests/fixtures/manifest to reflect the new default.
2. `e6e919f` — Remove the option entirely:
- Deleted `devCacheControlNoCache?: boolean` from `ExperimentalConfig`
interface, `config-schema.ts` Zod schema, `defaultConfig`,
`NextConfigRuntime`, and `getNextConfigRuntime()`.
- Replaced all four conditional ternaries in `base-server.ts`,
`router-server.ts`, `pages-handler.ts`, and `app-page.ts` with the
hard-coded string `'no-cache, must-revalidate'`.
- Deleted the `dev-cache-control-no-cache-disabled` test suite (was
testing the `false` path which no longer exists).
- Simplified the remaining `dev-cache-control-no-cache` test (removed
experimental framing).
- Synced `rspack-dev-tests-manifest.json`.
---------
Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
This makes sure we don't have assertions that will only pass in one
environment e.g. these should work for running against bun and not just
node or against a local metadata URL not just a deployed https fully
qualified domain. Ensures others can leverage this test suite for their
own verification easily.
### What?
Removes the deprecated `experimental_ppr` route-level configuration
option from Next.js.
### Why?
The PPR configuration feature has been deprecated in favor of the cache
components feature flag. The `experimental_ppr` route-level option no
longer has any effect since incremental PPR is not supported with cache
components.
Rather than silently ignoring this configuration, this change ensures
that users receive an explicit error when they attempt to export
`experimental_ppr` from their routes. This provides clear feedback that
the configuration needs to be removed when migrating to the cache
components approach.
### How?
- Removed `experimental_ppr` from the segment config schema and types
(both Rust and TypeScript)
- Updated `checkIsRoutePPREnabled` to no longer accept the `appConfig`
parameter since route-level config is removed
- Added `experimental_ppr` to the list of possibly invalid exports when
cache components are enabled, ensuring users get an error instead of
silent failure
- Removed unused `reduceAppConfig` import that was no longer needed
after these changes
As of #49343, in dev mode only, we accidentally change the `metadataBase` from an `URL` instance to a URL string, when passing it to child layouts or pages via the `parent` argument in `generateMetadata`. This is because we clone the metadata object via `JSON.stringify`/`parse` and then freeze it, to prevent relying on mutating resolved metadata. The replacer/reviver functions we used to clone the metadata didn't properly handle preserving `URL` instances as intended, so they were turned into strings.
Because user code already had to handle the case of `metadataBase` potentially being a string at runtime in dev mode, we can just officially reflect that as well in our types, not only for the `ResolvedMetadata` input type, but also for the `Metadata` output type.
This also enables `'use cache'` being used in `generateMetadata`, because React currently does not support transporting `URL` instances from one environment to another (e.g. `Server` to `Cache`).
Note that all other URL properties in `Metadata` and `ResolvedMetadata` already allowed `string`, so this only affects `metadataBase`.
To resolve the ambiguity between dev and prod, we're now always transforming all URL instances to strings, before passing the resolved metadata to `generateMetadata`. This is also reflected in the `ResolvedMetadata` type.
Current metadata files are compiled to `route.js` files. This happens
not only to metadata API (e.g., `icon.tsx`), but to static files as
well, which is unnecessary. In development, this leads to confusing logs
such as:
```
Compiled /favicon.ico in ...ms
```
Users may reasonably wonder why Next.js is "compiling" a static file.
Static files can be served directly from the filesystem, like
`_next/static/` assets. Therefore, in development, skip compilation of
static metadata files and serve them as static.
For production, we can follow up in a separate PR to skip compiling the
`route.js` files and emit static files to the dist directory, and also
serve from the filesystem there as well.
Closes [NEXT-4338](https://linear.app/vercel/issue/NEXT-4338)
---
TODO(s) to follow up:
- Fix Cache Components failing build with metadata files inside the
dynamic route.
- x-ref: [slack
thread](https://vercel.slack.com/archives/C06N3AXRQ82/p1738053808940719)
---------
Co-authored-by: Jiachi Liu <inbox@huozhi.im>
Viewport blocks the root while Metadata no longer does. However Metadata
requires the reading of dynamic params for things like icon loading and
other features that require the path whereas viewport does not. Reading
the path in fallback prerenders is considered dynamic when DIO is
enabled which means that any dynamic route with a file based metadata
such as an icon will trigger dynamic at the root.
To Address this we simply resolve viewport separately from metadata.
### What
Adds support for Pinterest's Rich Pin Data meta tag, while
future-proofing for additional Pinterest tags.
This currently works like so
```js
pinterest: {
richPin: true
}
```
And would resolve to:
```html
<meta property="pinterest-rich-pin" content="true" />
```
x-ref: https://help.pinterest.com/en/business/article/rich-pinsCloses#70873
---------
Co-authored-by: Dominik Koch <dominik@koch-bautechnik.de>
When resolving opengraph / twitter images, we currently ignore the configured `metadataBase` value, which goes against the [documentation](https://nextjs.org/docs/app/api-reference/functions/generate-metadata#default-value) and doesn't seem correct, since an explicit `metadataBase` should be preferred over a fallback.
This updates the handling to ensure an explicit `metadataBase` takes priority over the implicit path discovery.
The one remaining special case here is for the "static metadata" case, aka the `opengraph-image` convention. We'll continue to prefer the fallback `metadataBase` in preview Vercel deploys & locally.
Fixes#66957
Closes NDX-260
This enhances the current parsing completed by Next.js of configuration
from app segments. Previously a collection of fragile checks was used to
parse the different segment configuration options which performed no
validation on the inputs.
This uses the `zod` library (which we already use internally) to perform
validation on the configuration exported. A followup PR #70480 will add
more verbose error logging around the validation errors.
Next.js has a number of dynamic APIs that aren't available during
prerendering. What happens when you access these APIs might differ
depending on the mode you are using, for instance if you have PPR turned
on or the newly introduced `dynamicIO` experimental mode. But regardless
of the mode the underlying API represents accessing something that might
only be available at render time (dynamic rendering) rather than
prerender time (build and revalidate rendering)
Unfortunately our current dynamic APIs make certain kinds of modeling
tricky because they are all synchronous. For instance if we wanted to
add a feature to Next.js where we started a dynamic render before a
Request even hits the server it would be interesting to be able to start
working on everything that does not rely on any dynamic data and then
once a real Request arrives we can continue the render and provide the
associated Request context through our dynamic APIs.
If our dynamic APIs were all async we could build something like this
because they represnt a value that will eventually resolve to some
Request value. This PR updates most existing dynamic APIs to be async
rather than sync. This is a breaking change and will need to be paired
with codemods to realistically adopt. Additionally since this change is
so invasive I have implemented it in a way to maximize backward
compatibility by still allowing most synchronous access. The combination
of codemods, typescript updates, and backward compat functionality
should make it possible for projects to upgrade to the latest version
with minimal effort and then follow up with a complete conversion over
time.
#### `cookies()`
`cookies()` now returns `Promise<ReadonlyRequestCookies>`. Synchronous
access to the underlying RequestCookies object is still supported to
facilitate migration.
```tsx
// ------------ preferred usage
// async Server Component
const token = (await cookies()).get('token')
// sync Server Component
import { use } from 'react'
//...
const token = use(cookies()).get('token')
// ------------ temporarily allowed usage
// javascript, dev warning at runtime
const token = cookies().get('token')
// typescript, dev warning at runtime
import { type UnsafeUnwrappedCookies } from 'next/headers'
// ...
const token = (cookies() as unknown as UnsafeUnwrappedCookies).get('token')
```
#### `headers()`
`headers()` now returns `Promise<ReadonlyHeaders>`. Synchronous access
to the underlying Headers object is still supported to facilitate
migration.
```tsx
// ------------ preferred usage
// async Server Component
const header = (await headers()).get('x-foo')
// sync Server Component
import { use } from 'react'
//...
const header = use(headers()).get('x-foo')
// ------------ temporarily allowed usage
// javascript, dev warning at runtime
const header = headers().get('x-foo')
// typescript, dev warning at runtime
import { type UnsafeUnwrappedHeaders } from 'next/headers'
// ...
const header = (headers() as unknown as UnsafeUnwrappedHeaders).get('x-foo')
```
#### `draftMode()`
`draftMode()` now returns `Promise<DraftMode>`. Synchronous access to
the underlying DraftMode object is still supported to facilitate
migration.
```tsx
// ------------ preferred usage
// async Server Component
if ((await draftMode()).isEnabled) { ... }
// sync Server Component
import { use } from 'react'
//...
if (use(draftMode()).isEnabled) { ... }
// ------------ temporarily allowed usage
// javascript, dev warning at runtime
if (draftMode().isEnabled) { ... }
// typescript, dev warning at runtime
import { type UnsafeUnwrappedDraftMode} from 'next/headers'
// ...
if ((draftMode() as unknown as UnsafeUnwrappedDraftMode).isEnabled) { ... }
```
#### `searchParams`
`searchParams` is now a `Promise<{...}>`. Synchronous access to the
underlying search params is still supported to facilitate migration.
```tsx
// ------------ preferred usage
// async Page Component
export default async function Page({
searchParams
}: {
searchParams: Promise<{ foo: string }>
}) {
const fooSearchParam = (await searchParams).foo
}
// sync Page Component
import { use } from 'react'
export default function Page({
searchParams
}: {
searchParams: Promise<{ foo: string }>
}) {
const fooSearchParam = use(searchParams).foo
}
// ------------ temporarily allowed usage
// javascript, dev warning at runtime
export default async function Page({ searchParams}) {
const fooSearchParam = searchParams.foo
}
// typescript, dev warning at runtime
import { type UnsafeUnwrappedSearchParams } from 'next/server'
export default async function Page({
searchParams
}: {
searchParams: Promise<{ foo: string }>
}) {
const syncSearchParams = (searchParams as unknown as UnsafeUnwrappedSearchParams<typeof searchParams>)
const fooSearchParam = syncSearchParams.foo
}
```
#### `params`
`params` is now a `Promise<{...}>`. Synchronous access to the underlying
params is still supported to facilitate migration. It should be noted
that while params are not usually dynamic there are certain modes where
they can be such as fallback prerenders for PPR.
```tsx
// ------------ preferred usage
// async Segment Component
export default async function Layout({
params
}: {
params: Promise<{ foo: string }>
}) {
const fooParam = (await params).foo
}
// sync Segment Component
import { use } from 'react'
export default function Layout({
params
}: {
params: Promise<{ foo: string }>
}) {
const fooParam = use(params).foo
}
// ------------ temporarily allowed usage
// javascript, dev warning at runtime
export default async function Layout({ params}) {
const fooParam = params.foo
}
// typescript, dev warning at runtime
import { type UnsafeUnwrappedParams } from 'next/headers'
export default async function Layout({
params
}: {
params: Promise<{ foo: string }>
}) {
const syncParams = (params as unknown as UnsafeUnwrappedParams<typeof params>)
const fooSearchParam = syncParams.foo
}
```
### Typescript Changes
When using typescript with Next.js currently it is up to you to author
types for Pages, Layouts and other Segment components that recieve props
like `params` and `searchParams`.
Next comes with some build-time type checking to ensure you have not
improperly typed various top level module exports however the current
type assertions for `params` and `searchParams` is `any`. This isn't
very helpful because it allows you to erroneously type these props.
`searchParams` is tricky because while the default type is a dictionary
object parsed using node.js url parsing it is possible to customize when
running a custom Next.js server. However we can ensure that you
correctly type the prop as a Promise so with this change the validated
type for `searchParams` will be `Promise<any>`.
In the long run we will look at updating the `searchParams` underlying
type to be URLSearchParams so we can move away from supporting
customized parsing during rendering and we can get even more explicit
about valid types.
`params` is more straight forward because the framework controls the
actual `params` prop implementation and no customization is possible. In
the long run we want to enforce you are only typing params that are
valid for the Layout level your file is located in but for now we are
updating the allowed type to be `Promise<{[key: string]: string |
string[] | undefined }>`.
These new type restrictions may also require fixes before being able to
successfully build a project that updates to include these breaking
changes. These changes will also not always be codemodable because it is
valid to type the entire component using an opaque type like `Props`
which our codemods may not have an ability to introspect or modify.
### What
Support merging the static metadata file conventions `favicon.ico` with
other customized metadata icon from the module export.
### Why
`favicon.ico` should be displayed everywhere as it's the icon
representative of the website for all pages. Any customized `icon` in
metadata export `export const metadata` or `export function
generateMetadata()` shouldn't override the `favicon.ico` file
convention.
Fixes#55767
---------
Co-authored-by: hrmny <8845940+ForsakenHarmony@users.noreply.github.com>
Add support for the `fb:app_id` & `fb:admins` meta tag in the
generateMetaData function
---------
Co-authored-by: Sam Ko <sam@vercel.com>
Co-authored-by: Jiachi Liu <inbox@huozhi.im>
### What
app-dir `metadata.test.ts` is pretty big and includes a lot of erroring
tests and navigation tests. Breaking them into smaller suites to avoid
the erroring on effect on others.
- metadata
- metadata-navigation
- metadata-thrown
Moved the metadata testing utils into `next-test-utils` for sharing
purpose.
Moved the hmr test to the bottom to avoid flakyness.
### What
Optimizing the static generation for dynamic metadata routes
If you're not using `generateSitemaps()` or `generateSitemaps()`, you
don't need to change any file conventions.
If you're using multi sitemap routes, make sure the returned `id`
properties from `generateSitemaps()` don't need to contain `.xml`, since
we'll always append one for you.
Analyzing the exports of metadata routes and determine if we need to
make them as dynamic routes.
### Why
Previously, users are struggling with the multi routes of sitemap or
images.
For sitemap, the `.xml` extension in url doesn't get appended
consistently to the multi sitemap route between dev and prod.
For image routes, the generated image routes are always dynamic routes
which cannot get static optimized.
The reason is that we need to always generate a catch-all route (such as
`/icon/[[...id]]` to handle both single route case (e.g. without
`generateImageMetadata`, representing url `/icon`) or multi route (e.g.
with `generateImageMetadata`, representing url `/icon/[id]`), only
catch-all routes can do it. This approach fail the static optimization
and make mapping url pretty difficult as parsing the file to check the
module exports has to be done before it.
#### Benifits
For image routes urls, this approach could help on static generation
such as single `/opengraph-image` route can be treated as static, and
then it can get static optimized if possible.
**Before**: `/opengraph-image/[[...id]]` cannot be optimized
**After**: single route `/opengraph-image` and multi-route
`/opengraph-image/[id]` are both possible to be statically optimized
For sitemap, since we removed appending `.xml` for dynamic routes, it’s
hard for users to have `/sitemap.xml` url with dynamic route convention
`sitemap.js` . But users desire smooth migration and flexibility.
**Before**: In v15 rc we removed the `.xml` appending that `sitemap.js`
will generate url `/sitemap` makes users hard to migrate, as users need
to re-submit the new sitemap url.
**After**: Now we'll consistently generate the `.xml`. Single route will
become `/sitemap.xml`, and multi route will become `/sitemap/[id].xml`.
It's still better than v15 as the urls generation is consistent, no
difference between dev and prod.
Here's the url generation comparsion
#### Before
All the routes are dynamic which cannot be optimized, we only had a
hacky optimization for prodution build multi-routes sitemap routes
| | only default export | `export generateImageMetadata()` | `export
generateSitemaps()` |
| -- | -- | -- | -- |
| opengraph-image.js | /opengraph-image/[[...id]] |
/opengraph-image[[...id]]/ | /opengraph-image/[[...id]] |
| sitemap.js | /sitemap/[[...id]] | /sitemap/[[...id]] | dev:
`/sitemap/[[...id]]` prod: `/sitemap/[id]` |
#### After
Most of the single route will are to get statically optimized now, and
the multi-routes sitemap are able to get SSG now
| | only default export | `export generateImageMetadata()` | `export
generateSitemaps()` |
| -- | -- | -- | -- |
| opengraph-image.js | /opengraph-image | /opengraph-image/[id] | - |
| sitemap.js | /sitemap.xml | - | /sitemap/[id].xml |
Next.js will have less overhead of mapping urls, we can easily multiply
the urls generation simply based on file conventions.
x-ref: feedback from #65507Closes#66232
### What?
Because `createMultiHtmlMatcher` does not use `await` keyword internally
and is not an async function, all `await` in front of the match function
returned from `createMultiHtmlMatcher` was removed in
`test/e2e/app-dir/metadata/metadata.test.ts`.
The JSDoc for `createMultiHtmlMatcher` was also modified.
Co-authored-by: Jiachi Liu <inbox@huozhi.im>
### What
Follow up for #62873
Only apply credentials in metadata manifest tag for preview deployment
### Why
When the CORS header is "\*" and if you're including credentials in
request it will block by browser. For manifest's case, if it's fetching
from a publich CDN with responsing the CORS header "\*" and mismatching
with the current website domain it will get blocked. Previously we
introduced that change mostly for vercel deployment auth protection, now
we're going to check if it's preview deployment and also the same
origin, if yes then we'll include credentails for manifest request
Closes NEXT-3208
### What?
The string value of `Metadata.openGraph.emails` throws error:
```tsx
export const metadata = {
openGraph: {
type: 'article',
emails: 'author@vercel.com',
},
};
```
Error:
```sh
r.map is not a function
```
The type is:
```tsx
type OpenGraphMetadata = {
// ...
emails?: string | Array<string>
}
```
### Why?
Basic values such as `emails` and `phoneNumbers` were not included when
`ogType` was set while resolving the values.
### How?
Include basic values when resolving the metadata.
Fixes#63415
### What
Strip the search query for the `urlPathname` passed down to metadata
handling.
### Why
This is because the `urlPathname` from `staticGenerationStore` contains
query, so it will contain `?rsc` query for client navigation, which lead
to the relative path canonical url (e.g. `./`) will have the search
query along with it. This PR is to remove that and make sure always uses
pathname.
Reported by @pacocoursey
Closes NEXT-2963
This ensures we properly catch/handle `generateMetadata` errors during
eager evaluating of nested `generateMetadata` functions in the tree.
Previously if we eager evaluated a child metadata function that threw an
error e.g. `notFound()` and but the parent metadata function took longer
the thrown error would be an unhandled rejection causing the process to
crash depending on the environment.
Fixes: NEXT-2588
Closes NEXT-2786
We didn't set the property of manifest before, this PR fixes the missing
prop
> If the manifest requires credentials to fetch, the crossorigin
attribute must be set to use-credentials, even if the manifest file is
in the same origin as the current page.
x-ref: https://developer.mozilla.org/en-US/docs/Web/Manifest
Fixes NEXT-2706
### What
We should respect the `trailingSlash` config for metadata canonical url,
this PR is adding the handling for strip or keep the trailing slash for
canonical url. Passing down trailingSlash config to metadata resolving
to decide how we handle it.
### Why
The tricky one was `/` pathname, when visiting the origin directly, that
it will always have at least `/` in the URL instance. But for the
default `origin`, it shouldn't show the `/` if the `trailingSlash`
config is `false`. Also it should show trailing slash for all pathnames
if that config is enabled.
BTW there's a `__NEXT_TRAILING_SLASH` env but since we're using the
fixed nextjs runtime module, so this can't be dynamically replaced in
the metadata resolving modules. So we didn't use it
Fixes#54070
Closes NEXT-2424
## Story Time
Metadata API introduced two exports `metadata` and `generateMetadata` to the pages and layouts under app router, as partial prerendering work is going on and people are desiring to render the metadata asynchronizly, this change will be the preparation for moving to the dynamic & asynchronized land. In short, if we can render the metadata asynchronizedly, it will benefit the performance of the initial page loading and client page transiation a lot. Any slow data fetching can be handled while the essential page "shell" is rendered.
For meta tags, there're few ones will visually affect your web page, such as `<meta name="viewport">`, `<meta name="theme color">` and `<meta name="color-scheme">`, rendering them lately after the page frame is ready might bring flickering to the page such as revreting whole page's theme color or shaking due to viewport updates. Those meta are not majorly the "metadata" for SEO, but more for user experience when opening the page. If we're rendering everything as async meta tags, it won't be ideal due to the flickering on your web pages.
## Solution Preparation
We'll want to render the meta tags separately to make sure the visual ones are rendered as blocking along with web page, and then the ones for SEO or bots can be flushed later by later, like a suspense boundary keeps emitting them into the head of html.
We optionally picked up 3 meta tag "viewport", "theme-color" and "color-scheme" to be render first into the web page with html "shell", to guarantee the layout viewport and basic styling are rendered first.
This PR introduced two module export in the page and layout files: `viewport` and `generateViewport`, in order to separate the visual meta tags from the SEO metadata.
### API
```ts
// page.js | layout.js
export const viewport = {
// viewport meta tag
width: 'device-width',
initialScale: 1,
maximumScale: 1,
interactiveWidget: 'resizes-visual',
// visual meta tags
colorScheme: 'dark',
themeColor: { color: 'cyan', media: '(prefers-color-scheme: dark)' },
}
```
There's also a dynamic API like what we did for metadata API
```ts
// page.js | layout.js
export function generateViewport() {
return { ... }
}
```
## Notice
This PR won't get SEO metadata rendered asyncronizedly, instead it's a preparation for the later work in partial prerendering and async metadata. We'll encourage the Next.js community moving to the new metadata viewport API if you're customzing those 3 meta tags. Usually you don't have to change viewport itself, so mostly like only theme-color and color-scheme could relate to it.
### What?
- fixes test https://github.com/vercel/next.js/blob/17553c5e25c824ce04045066c24a8f138881473d/test/e2e/app-dir/metadata/metadata.test.ts#L487
The way next.js collects static metadata is read static metadata, and then read layout metadata to merge multiple metadatas into a single layout path (https://github.com/vercel/next.js/blob/17553c5e25c824ce04045066c24a8f138881473d/packages/next/src/lib/metadata/resolve-metadata.ts#L347-L352)
When turbopack creates LoaderTree for the corresponding directory tree, it extracts `page` but skips metadata in result there are orphan components that have a metadata doesn't have layout metadata, as well as a component have a layout doesn't have metadata. Latter is being rendered as a page (since it have correct layout), which eventually falls back to the default metadata instead.
PR trickles down the metadata when extracting page (creating a new component with `page`) to consolidates those.
Also PR expands Metadata to have base_page property to capture where it has been originally exists, as we clone down metadata then do `fillMetadataSegment` against the current page where LoaderTree is being created it creates a wrong relative path. For example, currently
```
/icon.svg
- opengragph/
- static -> path being `/opengraph/.../icon.svg` instead of `/icon.svg`
```
When recursively traverse directory tree, capture each components with corresponding base_page to calculate instead.
Unfortunately this doesn't make pass all of the metadata tests; there are lot to dig more. Would like to scope PR in a reasonable size.
Closes WEB-1795
A continuation of #51864 with RedirectType used in an e2e test and some
ts-ignores removed from the codebase. Happy to split it into two PRs but
I left comments in the two consequential files
---------
This is an improvement for twitter card, if there's image present we could use `summary_large_image` to make the twitter card look better instead of using `summary` by default, so that users could benefit from the large image card display on twitter
### What & Why
The dynamic not-found boundary didn't work as expected as it was using the `pathname` to match how many levels of the segements should be matched. For dynamic routes this doesn't work, unlike normal page, the unmatched segment can also hit the not found boundary in the same level.
### How
Use `segment` of loader tree nodes to determine if not-found boundary searching is reached to the end instead of using `pathname`.
> NOTE: For production `/_not-found` case since it's a valid page on production and still has the original tree, so we handle that as a special case to use the not found loader tree (with empty child routes) to render.
Fixes#53344
### What & Why
Using `notFound()` in `generateMetadata()` or in page will lead to unacught global not found error when you do navigation, this is because `head` cache is actually not inside the error boundary as designed to stay in the beginning of the content. But in this way we won't be able to catch the notFound error thrown from metadata API. So we created a new approach that can separate the error itself from metadata result, and throw as error under the error boundaries, and metadata itself will always be safe to render.
### How
We use a new promise that resolve the `error` thrown from metadata resolving, and let the render result always return successfully and always has a value, using default metadata when there's error thrown. Create two components, one rendering the metadata tags, and another `MetadataOutlet` to throw the error when the resolving is failed but rendered under error boundaries.
```jsx
const [MetadataTree, MetadataOutlet] = createMetadataComponents(/*...*/)
const ComponentTree = createComponentTree({ metadataOutlet: <MetadataOutlet /> })
renderRSC(
<AppRourer head={<MetadataTree />}>
<ComponentTree />
</AppRourer>
)
```
`metadataOutlet` will stay next to page component in the layout hierarchy
discuessed with @gnoff
Minor changes:
* When there's rendering root layout layer, not found boundary should be `DefaultNotFound` if the custom one doesn't exisit
Fixes#53371Fixes#53149
### What?
Change the `favicon.ico` metadata `sizes` property from always `"any"`
to using the actual size possible
### Why?
When chrome/firefox browsers search for icon it needs the proper
metadata to determine which one to use, giving `favicon.ico` sizes with
`"any"` will prevent it loading the `icon.svg` as default icon when
available.
Changing to set proper size of `favicon.ico` (as the `favicon.ico` sizes
could be 16x16, 32x32, etc.) fixes the issue.
It works (loading svg icon) for chrome and firefox:
Firefox
<img width="505" alt="image"
src="https://github.com/vercel/next.js/assets/4800338/6873e595-479d-4d9e-ae5c-39e5f938fcf5">
Chrome
<img width="460" alt="image"
src="https://github.com/vercel/next.js/assets/4800338/03bbe4c7-ef76-4f34-a611-337b0d4b97a3">
It loads favicon.ico for Safari:
Using the `test/e2e/app-dir/metadata` app it shows the favicon.ico for
Safari
<img width="1000" alt="image"
src="https://github.com/vercel/next.js/assets/4800338/92cc8714-ea5e-432d-8144-2a4a42ee4ce2">
Can't have it as an e2e test as I tested it always loads the
`favicon.ico` in headless mode which can't determine the behaviors
Fixes#52002
Co-authored-by: JJ Kasper <jj@jjsweb.site>
### What
This PR changes the flow of not-found rendering process.
### Why
`not-found.js` was rendered in two ways before:
* 1 is SSR rendering the not-found as 404
* 2 is triggering the error on RSC rendering then the error will be
preserved in inline flight data, on the client it will recover the error
and trigger the proper error boundary.
The solution has been through a jounery:
No top-level not found boundary -> introduce metadata API -> then we
create a top level root not found boundary -> then we delete it due to
duplicated rendering of root layout -> now this
So the solution before this PR is still having a root not found boundary
wrapped in the `AppRouter`, it's being used in a lot of places including
HMR. As we discovered it's doing duplicated rendering of root layout,
then we removed it and it started failing with rendering `not-found` but
missing root layout. In this PR we redesign the process.
### How
Now the rendering architecture looks like:
* For normal root not-found and certain level of not-found boundary
they're still covered by `LayoutRouter`
* For other error renderings including not-found
* Fully remove the top level not-found boundary, when it renders with
404 error it goes to render the fallback page
* During rendering the fallback page it will check if it should just
renders a 404 error page or render nothing and let the error from inline
flight data to trigger the error boundary
pseudo code
```
try {
render AppRouter > PageComponent
} catch (err) {
create ErrorComponent by determine err
render AppRouter > ErrorComponent
}
```
In this way if the error is thrown from top-level like the page itself
or even from metadata, we can still catch them and render the proper
error page based on the error type.
The problematic is the HMR: introduces a new development mode meta tag
`<meta name="next-error">` to indicate it's 404 so that we don't do
refresh. This reverts the change brougt in #51637 as it will also has
the duplicated rendering problem for root layout if it's included in the
top level not found boundary.
Also fixes the root layout missing issue:
Fixes#52718Fixes#52739
---------
Co-authored-by: Shu Ding <g@shud.in>
### What?
Support metadata exports for `not-found.js` conventions
### Why?
We want to define metadata such as title or description basic properties for error pages, including 404 and 500 which referrs to `error.js` and `not-found.js` convention. See more requests in #45620
Did some research around metadata support for not-found and error convention. It's possible to support in `not-found.js` when it's server components as currently metadata is only available but for `error.js` it has to be client components for now so it's hard to support it for now as it's a boundary.
### How?
We determine the convention if we're going to render is page or `not-found` boundary then we traverse the loader tree based on the convention type. One special case is for redirection the temporary metadata is not generated yet, we leave it as default now.
Fixes#52636
### What
This was an issue introduced in #52416 when we check the images and icons property while merging with static file conventions. Where we should check if the properties are present but we only checked if they value is falsy. So when you're using `icons` with array value it breaks.
Now we properly check all the possible case of `metadata.icons` value, so then decide if we are using the static file convention or the icons property of defined metadata.
Also add few more tests cases to cover icons resolving.