Steps
- Read the target page at
$ARGUMENTSand understand its data sources, mutations, and navigation. - Identify platform boundaries: keep identical page implementations wrapped in
packages/ui; use dependency injection only when website and desktop data sources differ. - Decide the category:
- Wrapped (
layouts/wrapped/) — if the page uses the same API source on both platforms (e.g. web requests, not Tauri plugins). Just move the page component intopackages/uiand import it from both frontends. - Shared (
layouts/shared/) — if the page has different data-fetching logic per platform (e.g. website usesapi-client, app uses Tauriinvoke). Requires a DI contract.
- Wrapped (
- For shared layouts:
- Define a DI contract interface in
providers/capturing all platform-specific operations. - Create the layout component that injects the context and handles all UI logic.
- Extract reusable stateful logic (search, filtering, selection) into
composables/. - Implement the contract separately in each frontend (
apps/website/,apps/app-frontend/).
- Define a DI contract interface in
- For wrapped pages:
- Move the page component into
packages/ui/src/layouts/wrapped/matching the route structure. - Replace any platform-specific imports with shared utilities.
- Import and render the wrapped page from both frontends as a simple component.
- If the layout uses TanStack Query for first paint, keep route-shell prefetch keys and fetchers identical to the layout queries.
- Move the page component into
- Verify the page renders correctly by checking for missing imports and that all DI contracts are satisfied.