cloudflare / cloudflare/vinext

Metadata: parent title.template is applied to a page-level string title (Next treats it as absolute)

Open
#3,065 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
8.8k
Forks
406
Avg merge
2d 6h
Merged PRs (30d)
120

Description

### What happened

vinext applies a parent layout's `title.template` to a page that exports a **plain string** `title`. Next.js 16 treats a page-level string title as absolute and does **not** apply the parent template, so the site name ends up duplicated under vinext.

```
Next.js 16.3.2 Home | Devfive
vinext Home | Devfive | Devfive

Next.js 16.3.2 홈 | 데브파이브
vinext 홈 | 데브파이브 | 데브파이브
```

Only the two root routes (`/en/`, `/ko/`) differ; other routes match, because only these set a page-level string title.

### Reproduction

`app/[lang]/layout.tsx`

```ts
export async function generateMetadata(): Promise {
return {
title: {
default: 'Devfive',
template: '%s | Devfive',
},
}
}
```

`app/[lang]/page.tsx`

```ts
export async function generateMetadata(): Promise {
return { title: 'Home | Devfive' }
}
```

- Next.js renders `Home | Devfive` — the string is absolute, template skipped.
- vinext renders `Home | Devfive | Devfive` — template applied on top.

Our application deliberately writes the full title at page level precisely because Next 16 does not apply the template there (we have a code comment saying so, added while fixing an accessibility requirement for unique page titles). Under vinext that workaround double-applies.

### Expected

Match Next.js: a page-level `title: string` is absolute; the parent `title.template` applies only to `title.default` resolution for child segments that do not set their own title, and to `title: { absolute: ... }` / nested title objects per the Metadata API rules.

### Why it matters

`` is user-visible in the tab and in search results, so this is an SEO/UX regression rather than a cosmetic difference. It was caught by a golden-master diff comparing SSR HTML between the two runtimes.

### Environment

- vinext 1.0.0-beta.8, Vite 8.2.2
- Next.js 16.3.2 API surface, App Router, `generateMetadata`
- `next-intl` for localization (the title strings come from a plain record, not from next-intl, so the plugin is not involved)
- `--platform=node`, `output: 'standalone'`

Contributor guide

Open the contributing guide

Research direction

Start with the reproduction in app/[lang]/layout.tsx and app/[lang]/page.tsx, then compare the SSR title output with Next.js 16.3.2. Trace vinext's metadata resolution for page-level string titles and parent templates. Done means /en/ and /ko/ produce the same non-duplicated titles as Next.js while other routes remain unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
nextjs, typescript
Domain
web-dev
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.