Skip to content

feat(router): layout slug props [breaking behavior]#2035

Merged
dai-shi merged 8 commits intomainfrom
feat/router/layout-slugs
Apr 30, 2026
Merged

feat(router): layout slug props [breaking behavior]#2035
dai-shi merged 8 commits intomainfrom
feat/router/layout-slugs

Conversation

@dai-shi
Copy link
Copy Markdown
Member

@dai-shi dai-shi commented Apr 23, 2026

close #1665

Summary

This PR adds slug props support for layouts by introducing a breaking change in static layout behavior.

Layout slug props

Before this PR, layouts only received children, even when the layout path itself contained dynamic segments.

With this PR, layouts receive slug props, but only for parent path.

Examples:

createLayout({
  render: 'dynamic',
  path: '/[lang]',
  component: ({ lang, children }) => (
    <div>
      <h1>{lang}</h1>
      <p>{children}</p>
    </div>
  ),
});

createPage({
  render: 'dynamic',
  path: '/[lang]/blog/[slug]',
  component: ({ slug }) => <h2>{slug}</h2>,
});

For /en/blog/hello:

  • the layout receives { lang: 'en' }
  • the layout does not receive slug
  • the page receives { lang: 'en', slug: 'hello' }

Breaking behavioral change

This PR also changes how static layouts are cached.

Previously, a static layout under a dynamic segment was effectively shared. That is no longer the case. Static layout is rendered and cached for each path.

So if we have routes like,

  • /en/blog/hello
  • /ja/blog/hello

The layout example above will render twice for /en and /ja.

Migration path for shared static UI: use a static slice

If your intent was to share one static wrapper across many slug variants, the recommended migration path is to move that shared part into a static slice.

Before

createLayout({
  render: 'static',
  path: '/[lang]',
  component: ({ children }) => (
    <div>
      <nav>Shared navigation</nav>
      <main>{children}</main>
    </div>
  ),
});

After

createPage({
  render: 'dynamic',
  path: '/[lang]/blog/[slug]',
  slices: ['shared-shell'],
  component: ({ slug }) => (
    <Slice id="shared-shell">
      <article>{slug}</article>
    </Slice>
  ),
});

createSlice({
  render: 'static',
  id: 'shared-shell',
  component: ({ children }) => (
    <div>
      <nav>Shared navigation</nav>
      <main>{children}</main>
    </div>
  ),
});

This keeps the shared wrapper static and reusable, while avoiding incorrect sharing of slug-dependent layout output. You can use the slice in a layout too rather than the page.

Notes

  • This change affects both dynamic and static layouts in createPages.
  • The key behavioral change is only for static layouts under dynamic segments.
  • This works for fs-router and managed mode too.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
waku Ready Ready Preview Apr 30, 2026 2:03pm

Request Review

@codesandbox-ci
Copy link
Copy Markdown

codesandbox-ci Bot commented Apr 23, 2026

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 23, 2026

Open in StackBlitz

npm i https://pkg.pr.new/waku@2035

commit: 7e457c8

@dai-shi
Copy link
Copy Markdown
Member Author

dai-shi commented Apr 30, 2026

@copilot resolve the merge conflicts in this pull request

Co-authored-by: dai-shi <490574+dai-shi@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 30, 2026

@copilot resolve the merge conflicts in this pull request

Resolved the merge conflict in e2e/create-pages.spec.ts (commit ab2cd9e). The conflict was between our new layout slug prop tests and a new test added in main (static layout under dynamic layout is pre-cached at build time). Both sets of tests are now included.

@dai-shi
Copy link
Copy Markdown
Member Author

dai-shi commented Apr 30, 2026

@copilot resolve the merge conflicts in this pull request

Co-authored-by: dai-shi <490574+dai-shi@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 30, 2026

@copilot resolve the merge conflicts in this pull request

Resolved the merge conflict in packages/waku/src/router/create-pages.tsx (commit cc16f2e). The conflict was between our layout slug prop additions (createLayoutPropsMapper, getLayoutIdPath, {layoutPath, layoutIdPath} structure) and main's cleanup of the "page parts" feature (removing ComponentList/ComponentEntry types and array page element handling). The merged result keeps our layout slug props while adopting main's simplifications.

@dai-shi dai-shi changed the title feat(router): layout slug props feat(router): layout slug props [breaking behavior] Apr 30, 2026
@dai-shi dai-shi merged commit b2323aa into main Apr 30, 2026
48 of 50 checks passed
@dai-shi dai-shi deleted the feat/router/layout-slugs branch April 30, 2026 15:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

layouts should use router props as pages do

2 participants