Skip to content

fix(metadata): prevent layout title template from applying to the same route-layer page - #3071

Open
NriotHrreion wants to merge 2 commits into
cloudflare:mainfrom
NriotHrreion:fix/issue-3065-same-layer-title-template
Open

fix(metadata): prevent layout title template from applying to the same route-layer page#3071
NriotHrreion wants to merge 2 commits into
cloudflare:mainfrom
NriotHrreion:fix/issue-3065-same-layer-title-template

Conversation

@NriotHrreion

@NriotHrreion NriotHrreion commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Closes #3065

Overview

This PR fixes an App Router metadata compatibility issue where vinext applied a leaf layout's title.template to a page on the same route-tree layer. Next.js only propagates that template to deeper child routes.

This restores SSR output parity with Next.js, avoiding related SEO and accessibility regressions.

What changed

Added a field stashesTitleTemplate in MetadataMergeEntry to mark the entries on the same route-tree layer, preventing them from applying the layout's title template.

In packages/vinext/src/server/app-page-head.ts, filter the same route-layer pages.

const metadataEntries: MetadataMergeEntry[] = [
  ...layoutMetadataResults.flatMap((metadata, index) => {
    if (!metadata) return [];

    const isSameLayerAsPage =
      hasPrimaryPageLayer && layoutInputs[index]?.treePosition === routeSegments.length;
    return [
      {
        metadata,
        ...(isSameLayerAsPage ? { stashesTitleTemplate: false } : {}),
      },
    ];
  }),
  // ...other entries
];

Testing

  • pnpm test tests/app-page-head.test.ts tests/nextjs-compat/metadata.test.ts
  • playwright test tests/e2e/app-router/nextjs-compat/metadata.spec.ts --project=app-router
@pkg-pr-new

pkg-pr-new Bot commented Aug 24, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@3071
npm i https://pkg.pr.new/create-vinext-app@3071
npm i https://pkg.pr.new/@vinext/types@3071
npm i https://pkg.pr.new/vinext@3071

commit: b825d90

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0a4ba4e96e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +786 to +787
const isSameLayerAsPage =
hasPrimaryPageLayer && layoutInputs[index]?.treePosition === routeSegments.length;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Suppress same-layer templates in parallel route branches

When the primary page has no title and an active parallel slot supplies the title, this check only marks layouts from the primary layoutInputs. A slot whose leaf layout and page share a tree position is flattened from resolveParallelRouteMetadata without stashesTitleTemplate: false, so the slot layout still wraps its same-layer page title—for example, producing Slot page | Slot instead of Slot page. Apply the same layer-aware suppression while constructing parallel-route merge entries as well.

AGENTS.md reference: AGENTS.md:L84-L86

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared b825d90 against base 20fdac4 using alternating same-runner rounds. Next.js was unchanged and skipped.

0 improved · 0 regressed · 6 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 141.4 KB 141.4 KB ⚫ +0.0%
Client entry size (gzip) vinext 128.8 KB 128.8 KB ⚫ +0.0%
Dev server cold start vinext 3.19 s 3.15 s ⚫ -1.1%
Production build time vinext 3.66 s 3.64 s ⚫ -0.3%
RSC entry closure size (gzip) vinext 115.6 KB 115.7 KB ⚫ +0.1%
Server bundle size (gzip) vinext 196.8 KB 196.9 KB ⚫ +0.0%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant