TanStack Router for NativeScript apps.
Early Developer Preview
Current framework support:
solidvia@nativescript/tanstack-router/solid
The package is structured to support any framework (react, vue, svelte, angular).
npm install @nativescript/tanstack-router @tanstack/solid-router @tanstack/historyPeer dependencies expected in the host app:
@nativescript/core@nativescript-community/solid-jssolid-js
Use framework-scoped imports:
import { Link, createNativeScriptRouter } from '@nativescript/tanstack-router/solid'import {
createNativeScriptRouter,
createRootRoute,
createRoute,
NativeScriptRouterProvider,
} from '@nativescript/tanstack-router/solid'
const rootRoute = createRootRoute()
const homeRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/',
component: Home,
})
const aboutRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/about',
component: About,
})
const routeTree = rootRoute.addChildren([homeRoute, aboutRoute])
const router = createNativeScriptRouter({
routeTree,
initialPath: '/',
})
declare module '@nativescript/tanstack-router/solid' {
interface Register {
router: typeof router
}
}
export function App() {
return (
<NativeScriptRouterProvider
router={router}
actionBarVisibility="always"
/>
)
}import { Link } from '@nativescript/tanstack-router/solid'
<Link to="/about">
<label text="Go to About" />
</Link>
// Parameterized route (type-safe)
<Link to="/posts/$postId" params={{ postId: '123' }}>
<label text="Open Post 123" />
</Link>Link route typing is validated against your registered router when you add:
declare module '@nativescript/tanstack-router/solid' {
interface Register {
router: typeof router
}
}For parameterized routes, prefer route patterns + params:
- Use:
to="/posts/$postId"withparams={{ postId: id }} - Avoid:
to={`/posts/${id}`}
With strict typing, invalid paths (for example to="/invalid") produce TypeScript errors.
Use back to perform native-style stack pop (history back) instead of pushing to to.
<Link back fallbackTo="/posts">
<label text="Back to Posts" />
</Link>Behavior:
- If history can go back: runs
router.history.back() - If history cannot go back: navigates to
fallbackTo - If
fallbackTois omitted andtois provided: navigates toto
The provider supports a single modal route encoded in search params, following TanStack Router slot-RFC semantics:
/posts?@modal=/users/123
When @modal is present:
- The provider opens a NativeScript modal via
showModal() - The modal path is matched against your route tree (same route definitions)
- A modal-local router instance renders the modal route component
When @modal is removed:
- The provider closes the active NativeScript modal via
closeModal()
If the user closes the modal natively, router search state is synced back by
removing @modal.
NativeScriptRouterProvider supports modal presentation configuration with
either:
- A static
modalOptionsobject (applied to all modal routes), or - A
modalOptionsresolver function (applied per modal route/path)
This is useful for choosing different iOS presentation styles and sheet behavior for different modal routes.
<NativeScriptRouterProvider
router={router}
modalOptions={({ modalPath, routeId }) => {
if (routeId === '/users/$userId') {
return {
ios: {
presentationStyle: UIModalPresentationStyle.PageSheet,
detents: ['medium', 'large'],
selectedDetent: 'medium',
prefersGrabberVisible: true,
preferredCornerRadius: 20,
transparentBackgroundOnIOS26: true,
},
}
}
if (modalPath.startsWith('/full-screen-modal')) {
return {
fullscreen: true,
ios: {
presentationStyle: UIModalPresentationStyle.FullScreen,
transparentBackgroundOnIOS26: false,
},
}
}
return undefined
}}
/>modalOptions supports:
animated?: booleanfullscreen?: booleanios?: { ... }presentationStyle?: numberdetents?: ('medium' | 'large')[]selectedDetent?: 'medium' | 'large'prefersGrabberVisible?: booleanprefersScrollingExpandsWhenScrolledToEdge?: booleanprefersEdgeAttachedInCompactHeight?: booleanpreferredCornerRadius?: numbertransparentBackgroundOnIOS26?: boolean
You can set/remove @modal directly via search:
<Link to="/posts" search={{ ['@modal']: '/users/123' }}>
<label text="Open User Modal" />
</Link>Or use optional Link helpers:
modalTo="/users/123"to set@modalcloseModalto clear@modal
Use onTap to run custom logic and optionally cancel default navigation by returning false.
<Link
to="/posts"
onTap={() => {
if (!canOpenPosts()) {
return false // Skip default Link navigation
}
}}
>
<label text="Open Posts" />
</Link>Verbose internal router logs are disabled by default.
Enable them explicitly:
<NativeScriptRouterProvider
router={router}
debug={true}
/>createNativeScriptHistory
NativeScript-specific:
createNativeScriptHistorycreateNativeScriptRouterNativeScriptRouterProviderLink
Types:
NativeScriptModalDetentNativeScriptModalIOSPresentationOptionsNativeScriptModalPresentationOptionsNativeScriptModalOptionsResolverNativeScriptModalOptionsResolverContext
Re-exported from @tanstack/solid-router:
- Route creation APIs (
createRootRoute,createRoute,createFileRoute, etc.) - Router APIs (
createRouter, hooks, helper components)
- The router uses memory history under the hood.
- Native back navigation is synchronized with router history.
- Route transitions are driven through NativeScript
Frame.navigate()/Frame.goBack().