Skip to content

feat: add optional shellComponent to root route #4528

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 27, 2025

Conversation

schiller-manuel
Copy link
Contributor

No description provided.

Copy link

nx-cloud bot commented Jun 26, 2025

View your CI Pipeline Execution ↗ for commit e62a811.

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 4m 45s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 1m 39s View ↗

☁️ Nx Cloud last updated this comment at 2025-06-27 20:12:10 UTC

Copy link

@Copilot 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

This PR introduces an optional shellComponent hook on root routes, allowing consumers to wrap all route content in a custom layout component. Key changes include:

  • Extending core route types to include a shellComponent field.
  • Updating Solid- and React-specific Match components to render the shell wrapper when on a root route.
  • Adjusting example apps to demonstrate usage of shellComponent.

Reviewed Changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/solid-router/src/route.tsx Augment RootRouteOptionsExtensions with shell API
packages/solid-router/src/Match.tsx Wrap root matches in shellComponent
packages/router-core/src/route.ts Add default and extension interfaces for shell
packages/router-core/src/index.ts Export the new extension interface
packages/react-router/src/route.tsx Mirror shell API and adjust React types
packages/react-router/src/index.tsx Remove now-unneeded ReactNode alias
packages/react-router/src/Matches.tsx Replace ReactNode alias with React.ReactNode
packages/react-router/src/Match.tsx Wrap root matches in shellComponent
examples/solid/start-basic/src/routes/__root.tsx Update root example to use shellComponent
examples/react/start-basic/src/routes/__root.tsx Update root example to use shellComponent

Comment on lines 63 to 65
shellComponent: RootDocument,
})

function RootComponent() {
return (
<RootDocument>
<Outlet />
</RootDocument>
)
export function Layout({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}

function RootDocument({ children }: { children: React.ReactNode }) {
Copy link
Preview

Copilot AI Jun 26, 2025

Choose a reason for hiding this comment

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

The Layout component is declared but never used. Consider removing it or wiring it up (e.g., using it as the shellComponent) to avoid dead code in the example.

Copilot uses AI. Check for mistakes.

Copy link

pkg-pr-new bot commented Jun 26, 2025

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@4528

@tanstack/directive-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@4528

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@4528

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@4528

@tanstack/react-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@4528

@tanstack/react-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@4528

@tanstack/react-router-with-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-with-query@4528

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@4528

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@4528

@tanstack/react-start-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-plugin@4528

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@4528

@tanstack/router-cli

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@4528

@tanstack/router-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@4528

@tanstack/router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@4528

@tanstack/router-devtools-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@4528

@tanstack/router-generator

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@4528

@tanstack/router-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@4528

@tanstack/router-utils

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@4528

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@4528

@tanstack/server-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@4528

@tanstack/solid-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@4528

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@4528

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@4528

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@4528

@tanstack/solid-start-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-plugin@4528

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@4528

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@4528

@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@4528

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@4528

@tanstack/start-server-functions-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-client@4528

@tanstack/start-server-functions-fetcher

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-fetcher@4528

@tanstack/start-server-functions-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-server@4528

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@4528

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@4528

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@4528

commit: e62a811

@schiller-manuel schiller-manuel merged commit eb28ad0 into main Jun 27, 2025
5 checks passed
@schiller-manuel schiller-manuel deleted the feat-shellComponent branch June 27, 2025 20:17
schiller-manuel added a commit that referenced this pull request Jul 7, 2025
TypeScript cannot infer the return type of a component if it directly (e.g. inside a fragment) returns route state.

```tsx
const fooRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: 'foo/$id',
  validateSearch: (search) => ({ hello: search.hello }) as { hello: string },
  component: () => <>foo {fooRoute.useParams().id}</>,
})
```

This results in the following compiler error:

> 'component' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.ts(7023)

This commit reverts the change in #4528 that required the return type of a route component to be of `React.ReactNode` and uses `any` again.

Strict typing of the route components can be enabled via module augmentation:

```tsx
declare module '@tanstack/react-router' {
  interface RouteTypes<TProps> {
    component: React.FC<TProps>
  }
}
```

In the strict case, the above example compiles with an explicit return type annotation:

```tsx
const fooRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: 'foo/$id',
  validateSearch: (search) => ({ hello: search.hello }) as { hello: string },
  component: () : React.JSX.Element => <>foo {fooRoute.useParams().id}</>,
})
```

fixes #4560
@Nandan1996
Copy link

Hi @schiller-manuel i was trying spa mode, after build the _shell.html in output doesn’t include html & body element rendered by component option given to createRootRoute.

is it not the expected behaviour or shellComponent is expected to be used for this?
The spa mode doc doesn’t have any reference to shellComponent instead component option is used throughout the doc.

@schiller-manuel
Copy link
Contributor Author

@Nandan1996 please create a new issue including a complete minimal example

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants