Rebounder Tech Blog

Written by the people who actually run these systems in production.

metadata.title duplicates layout's title.template suffix

Published About 3 min readBy the Rebounder engineering team — the people who operate these systems

This article may contain affiliate links. Its content is not affected by advertising.

In short

When a parent layout sets title.template, a child page's metadata.title that still contains that suffix gets substituted whole into %s, so the suffix shows up twice in the title tag.

The short version

Under a parent layout that sets title.template, writing a child page’s metadata.title with the same suffix already appended will make that suffix appear twice in the title tag. The template never inspects the string it receives — it just substitutes whatever the child page returns into %s. The fix is to strip the suffix from the child page’s title and let the layout’s template add it alone.

What it looks like

In Next.js App Router, setting metadata.title as an object with a template on the parent layout.tsx automatically appends a shared suffix to whatever title each child page returns.

// app/layout.tsx
export const metadata: Metadata = {
  title: {
    default: "〇〇教室 東京",
    template: "%s | サイト名",
  },
  // ...
};

With that in place, the recruiting page’s metadata.title was written like this:

// app/recruit/page.tsx (before the fix)
export const metadata: Metadata = {
  title: "アルバイト募集(講師) | サイト名",
  description: "...",
};

Only on this page, the title shown in the browser tab and in Google’s search results came out as “アルバイト募集(講師) | サイト名 | サイト名” — the same suffix repeated twice in a row.

Why

template: "%s | サイト名" is a plain substitution: it drops whatever string the child page returns into %s without looking at its contents. As long as the child page’s title doesn’t already include the suffix, %s receives only the suffix-free string, and the result is a single “body | サイト名”.

But the recruiting page’s title already contained the literal suffix string | サイト名. So that entire string landed in %s, and combined with the template’s own suffix, the suffix effectively showed up twice across the final title.

The template mechanism itself worked exactly as designed. What broke was the unstated assumption that “child pages don’t write the suffix” — and that assumption held everywhere except this one page.

Fixing it

Strip the suffix out of the child page’s title and leave appending it entirely to the layout’s template.

// app/recruit/page.tsx (after the fix)
export const metadata: Metadata = {
  title: "アルバイト募集(講師)",
  description: "...",
};

layout.tsx’s template: "%s | サイト名" stayed untouched — removing the suffix from the string the child page returns was the entire fix. There was no need to touch or remove the template itself; it had been working correctly all along, so the side that needed fixing was always the “value being substituted,” i.e. the child page’s title.

Preventing a repeat

A design built around title.template can’t have TypeScript enforce the rule “child pages don’t write the suffix.” metadata.title is just a string, so any content passes type checking without complaint.

The only structural way to prevent this kind of mistake is to make sure nobody has to keep the template’s suffix in mind while writing a child page’s title in the first place. A plain, manual check — previewing how a page’s title will actually render, or eyeballing whether a suffix string has crept into the child page’s own title — is the one thing that needs to happen once, right when the template is set up.

Frequently asked questions

Q1Why does the suffix double when title.template is already set?

The parent layout's title.template substitutes the child page's metadata.title string straight into %s with no deduplication of any kind. If the child's title already contains the suffix, that whole string lands in %s and combines with the template's own suffix, so it appears twice.

Q2Is the fix to remove title.template?

No. The template itself works as designed, so there is nothing to remove there. Fix the child page's metadata.title instead: write it without the suffix and let the layout's template add it alone.

Q3Can this happen on other pages too?

Yes, on any page under a layout that sets title.template. Whenever a child page's own title string happens to include the same text the template appends as a suffix, that page's title tag shows it twice, regardless of which page it is.

Environment verified

  • Next.js 15.4 / App Router
  • Fixed on 2026-07-08

What this article is based on

  • TypeScript file lines 9-9commit 403e01a
  • TypeScript file lines 9-9commit f3ed55e
  • TypeScript file lines 16-19commit f3ed55e

Every claim in this article comes from the records above. The repositories we operate are private so we cannot link to them, but which file, which lines, and at which commit we read them is recorded for every article. Nothing here is written from guesswork.