Skip to content

doc: document named slots in layouts via inline components#8600

Open
Copilot wants to merge 4 commits intomainfrom
copilot/add-documentation-for-named-slots
Open

doc: document named slots in layouts via inline components#8600
Copilot wants to merge 4 commits intomainfrom
copilot/add-documentation-for-named-slots

Conversation

Copy link
Copy Markdown

Copilot AI commented May 1, 2026

Qwik City layouts can define multiple named <Slot name="..." /> areas, but pages exported as component$ create an opaque component boundary — the router passes the page as a single child, so q:slot attributes inside its rendered output are invisible to the layout's slot system.

The fix is to export the page as an inline component (plain function). Qwik's flatVirtualChildren/splitProjectedChildren (in render-ssr.ts) calls plain functions directly and flattens their JSX, making q:slot attributes visible to the parent layout.

Changes

  • packages/docs/src/routes/docs/(qwikcity)/layout/index.mdx — adds a new "Named Slots in Layouts" section covering:
    • Why component$ pages can't target layout named slots (Virtual node boundary)
    • The inline component workaround with a complete layout + page example
    • How to layer component$ children inside the inline default export for pages that need hooks (useSignal, useStore, etc.)
    • The nested layouts constraint: intermediate layouts must also be inline components rendering {children} top-level inside a fragment (not wrapped in any HTML element), so q:slot attributes propagate all the way up to the outer layout's slot system
    • What breaks propagation (wrapping {children} in an HTML element stops flattening) and the component$ + <Slot /> alternative when a wrapper element is genuinely needed

Pattern

// layout.tsx — named slots defined normally
export default component$(() => (
  <div>
    <Slot name="breadcrumb" />
    <main><Slot /></main>
    <aside><Slot name="sidebar" /></aside>
  </div>
));

// products/layout.tsx — intermediate layout must be inline, {children} top-level in a fragment
export default ({ children }: any) => (
  <>
    <nav q:slot="breadcrumb"><a href="/">Home</a> / Products</nav>
    {children} {/* top-level — q:slot attributes inside are still visible to outer layout */}
  </>
);

// products/index.tsx — plain function default export; Qwik inlines its output
//                      so q:slot attributes reach the outer layout's slot system
export default () => (
  <>
    <aside q:slot="sidebar"><p>Category filters</p></aside>
    <div><h1>All Products</h1></div>   {/* → default <Slot /> */}
  </>
);

For reactive pages, wrap stateful parts in a component$ rendered inside the inline shell — hooks are unavailable in inline components directly.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 1, 2026

⚠️ No Changeset found

Latest commit: 5685fca

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copilot AI linked an issue May 1, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Add documentation for named slots in layouts doc: document named slots in layouts via inline components May 1, 2026
Copilot AI requested a review from wmertens May 1, 2026 08:17
@wmertens
Copy link
Copy Markdown
Member

wmertens commented May 1, 2026

@copilot also document the constraint that if you have nested layouts, that intermediate layouts have to be inline components too, rendering {children} instead of <Slot />, and that those {children} have to be top-level (so inside a fragment).

Copy link
Copy Markdown
Author

Copilot AI commented May 1, 2026

@copilot also document the constraint that if you have nested layouts, that intermediate layouts have to be inline components too, rendering {children} instead of <Slot />, and that those `{ch...

Added in 53e1ac6. The new "Nested layouts with named slots" subsection documents:

  • Intermediate layouts must be inline components rendering {children} inside a fragment (not an HTML element)
  • Explains why: _flatVirtualChildren recurses into inline functions but stops at HTML elements, so wrapping {children} in e.g. a <div> hides the inner q:slot attributes from the outer layout
  • Shows ❌ broken pattern (div wrapper) and ✅ alternative (use component$ + <Slot /> if a wrapper element is genuinely needed)

Removed example code for wrapping elements and clarified usage of component$ with Slot.
@wmertens wmertens marked this pull request as ready for review May 1, 2026 10:40
@wmertens wmertens requested a review from a team as a code owner May 1, 2026 10:40
Copilot AI review requested due to automatic review settings May 1, 2026 10:40
@wmertens wmertens enabled auto-merge (squash) May 1, 2026 10:40
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Documents the Qwik City “inline component” technique required for pages (and intermediate layouts) to target named <Slot name="..."/> areas in parent layouts, explaining why component$ pages create an opaque boundary for slot projection.

Changes:

  • Adds a new “Named Slots in Layouts” section explaining the component boundary issue and the inline-default-export workaround.
  • Provides examples for: named-slot layouts, inline pages targeting named slots, nested layouts propagation rules, and a hook-friendly pattern (inline shell + component$ child).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


### Using hooks inside an inline page

Inline components cannot use Qwik hooks (`useSignal`, `useStore`, `useTask$`, etc.) directly. If your page needs reactive state, extract that part into a `component$` and render it inside the inline component:
Comment on lines +244 to +246
export default ({ children }: any) => (
<>
{/* contributes to the outer layout's "breadcrumb" slot */}
)
```

You will either need to repeat the Slot structure, or move the wrapper div to the child component.
- mrhoodz
- aendel
- jemsco
- copilot
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 1, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@builder.io/qwik@8600
npm i https://pkg.pr.new/@builder.io/qwik-city@8600
npm i https://pkg.pr.new/eslint-plugin-qwik@8600
npm i https://pkg.pr.new/create-qwik@8600

commit: 5685fca

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 1, 2026

built with Refined Cloudflare Pages Action

⚡ Cloudflare Pages Deployment

Name Status Preview Last Commit
qwik-docs ✅ Ready (View Log) Visit Preview 5685fca

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.

[📖] document named slots in layouts

3 participants