effector / effector/router

RFC: preserve shared layout instances across withLayout route transitions

Open
#57 0 comments 1 reaction 1 assignee Claimed by @sergeysova View on GitHub
enhancement RFC scope:react scope:solid scope:vue
Dominant language
TypeScript
Stars
8
Forks
2
Avg merge
13h 44m
Merged PRs (30d)
1

Description

## Problem

`withLayout(Layout, views)` creates a separate wrapper view for every route view. When `createRoutesView` switches between two views from the same `withLayout` group, the previous wrapper is unmounted and the next wrapper is mounted.

As a result, the shared layout is not actually persistent:

- layout-local state is reset;
- effects and subscriptions are torn down and recreated;
- providers, navigation, sidebars, and other expensive shared UI remount;
- transitions between pages with the same visual shell can flicker.

The current React documentation explicitly notes that `withLayout` applies the same markup but does not guarantee that the layout remains mounted. A parent route with `Outlet` is a workaround, but it requires changing the route tree and does not naturally cover arbitrary route groups or exclusions.

Related documentation task: #29.

## Reproduction

```tsx
const RoutesView = createRoutesView({
routes: [
...withLayout(DashboardLayout, [
createRouteView({ route: routes.dashboard, view: DashboardPage }),
createRouteView({ route: routes.settings, view: SettingsPage }),
]),
createRouteView({ route: routes.signIn, view: SignInPage }),
],
});
```

1. Track mount and cleanup in `DashboardLayout`.
2. Navigate from `dashboard` to `settings`.
3. Observe that `DashboardLayout` is unmounted and mounted again, even though both views belong to the same layout group.

## Decision to make

Define whether persistent layout identity is part of the public RouteView contract and where it belongs:

- change `withLayout` from a per-view wrapper transform into a grouped/persistent view primitive;
- represent the layout as a parent RouteView and switch its content through an outlet;
- introduce an explicit layout/group identity that `createRoutesView` can preserve;
- or keep `withLayout` as a markup-only helper and introduce a separate persistence API.

The decision should also define:

- entering and leaving a layout group;
- multiple groups using the same layout component;
- nested and lazy route views;
- routes that intentionally have no layout;
- whether behavior is shared across React, Solid, and Vue or binding-specific;
- preservation of RouteView metadata such as `children`.

## Expected result

- Navigating between views in the same layout group does not remount the layout.
- The page content changes normally.
- Navigating to a view outside the group unmounts the layout.
- Regression tests count layout mounts/unmounts and cover group entry, internal transition, and exit.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.