Skip to content

Render router layouts during SSR - #74

Merged
murerkinn merged 2 commits into
dashersw:mainfrom
murerkinn:main
Jun 9, 2026
Merged

Render router layouts during SSR#74
murerkinn merged 2 commits into
dashersw:mainfrom
murerkinn:main

Conversation

@murerkinn

@murerkinn murerkinn commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added support for query-mode layouts in SSR, enabling tab/parameter-based layout selection server-side.
  • Bug Fixes

    • Router layouts now render in initial SSR HTML output instead of only appearing after client hydration.
  • Tests

    • Added comprehensive test coverage for nested layout chains and query-mode rendering in SSR.

murerkinn added 2 commits June 9, 2026 16:31
The SSR router only resolved the leaf component for a route — layouts
declared via `layout:` on route groups were dropped, and the leaf was
mounted directly into the RouterView host div. Layout components like
AppShell and DashboardLayout only appeared after hydration, producing a
tree mismatch that forced the client to re-render the RouterView's
subtree on load.

Walk route groups in createServerRouter to collect a layout chain
(outermost → innermost) and capture queryModes metadata; resolve
mode-query children by their query-string param instead of by path
segment. createSSRRouterState.getComponentAtDepth now mirrors the
client Router contract: layouts at depth < layoutCount (with
page/route/params plus activeKey/keys/navigate for query-mode depths),
leaf at depth === layoutCount.

Expose `layout` and `mode` on the SSR RouteGroup type so the route
shape the router actually supports type-checks; the legacy `component`
field stays as a deprecated alias. Fix a broken vite-plugin-gea import
in the ssr/router-v2 example so its dev server starts.
@murerkinn
murerkinn requested a review from dashersw June 9, 2026 13:32
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR adds SSR support for router layout chains. createServerRouter now collects layouts from nested route groups into a chain and tracks query-mode metadata. SSRRouterState's getComponentAtDepth is rewritten to return layout components at each depth with appropriate props, enabling layouts to render in the initial server output instead of only after hydration.

Changes

SSR Router Layout Chain

Layer / File(s) Summary
Type contracts and interfaces
packages/gea-ssr/src/types.ts, packages/gea-ssr/src/server-router.ts, packages/gea-ssr/src/ssr-router-context.ts
RouteQueryMode type added to RouteGroup for query-driven child selection. ServerRouteResult extended with layouts: GeaComponentConstructor[] and queryModes: Map<number, ServerRouteQueryMode>. New RouteHostItem interface for depth-based component lookup. SSRRouterState.getComponentAtDepth(depth) signature changes to accept depth parameter and return RouteHostItem | null.
Server route resolution with layout chain
packages/gea-ssr/src/server-router.ts
resolveRoutes refactored to accept ResolveContext bundling query and skipGuards. Layout chains accumulated through nested route groups; query-mode groups select active children by query parameter and record queryModes per layout depth. Layout and query-mode metadata propagated through redirects, guards, wildcard matches, and leaf components. createServerRouter passes context object and returns layouts and queryModes in ServerRouteResult.
SSR router state component depth lookup
packages/gea-ssr/src/ssr-router-context.ts
createSSRRouterState extracts layouts and layoutCount from route result, initializes queryModes, treats route result component as leaf. getComponentAtDepth rewritten to return layout components at depth < layoutCount with route/params/page props and query-mode data (activeKey, keys, cacheKey), leaf at depth === layoutCount, null otherwise.
Server router layout chain tests
packages/gea-ssr/tests/server-router.test.ts
New test suite validates layout accumulation across nested groups, empty layouts for flat routes, query-mode child selection by parameter, fallback behavior, and layout preservation through wildcard matches.
SSR context component resolution tests
packages/gea-ssr/tests/ssr-router-context.test.ts
Unit tests verify layoutCount defaults to 0 when no layouts provided; getComponentAtDepth(0) returns leaf with cacheKey: null when no layouts; multiple layouts return correct component at each depth with proper props and null beyond chain; query-mode metadata surfaces activeKey, keys, and cacheKey; null leaf returns null at depth 0.
Router-v2 SSR integration tests
examples/ssr/router-v2/tests/router-v2-ssr.spec.ts
Playwright tests verify nested layout chain (AppShell → DashboardLayout → Overview) appears in raw HTML for /dashboard without client mount, and query-mode layout with "Billing" tab renders in HTML for /settings?tab=billing.
Example Vite configuration updates
examples/ssr/*/vite.config.ts
geaPlugin import updated from package root entry to source entry (.../vite-plugin-gea/src/index.ts) across flight-checkin, kanban, router-v2, and todo projects.
Release documentation
.changeset/gea-ssr-router-layout-chain.md
Changeset documenting patch release with layout chain collection, depth-aware getComponentAtDepth mirroring client Router, and RouteGroup type extensions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Layout chains now bloom in server HTML,
No waiting for the client to fulfill,
Depth-aware rendering, query modes dance,
SSR layouts get their rightful chance!
⚡ The rabbit hopped through nested groups with glee!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title directly and clearly describes the main objective: enabling router layouts to be rendered during server-side rendering, which aligns with the primary changes in the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@murerkinn
murerkinn merged commit c11bcec into dashersw:main Jun 9, 2026
4 of 7 checks passed
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.

1 participant