npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@octanejs/tanstack-router

v0.3.2

Published

TanStack Router bindings for the octane renderer — reuses @tanstack/router-core and swaps the React binding for octane's hooks.

Readme

@octanejs/tanstack-router

TanStack Router for the octane UI framework.

Installation

npm install @octanejs/tanstack-router
pnpm add @octanejs/tanstack-router

TanStack Router splits a framework-agnostic core (@tanstack/router-core — the router, route tree, matching, history, and the reactive store) from a thin React binding (@tanstack/react-router). Mirroring @octanejs/tanstack-query, this package re-exports the core verbatim and reimplements the framework binding on octane's hooks. The main runtime surface follows @tanstack/react-router, so most router code works by changing the import.

import {
  createRouter,
  createRootRoute,
  createRoute,
  RouterProvider,
  Outlet,
  Link,
  useParams,
} from '@octanejs/tanstack-router';
import { createRoot } from 'octane';

const rootRoute = createRootRoute({ component: RootLayout });
const indexRoute = createRoute({ getParentRoute: () => rootRoute, path: '/', component: Home });
const itemRoute = createRoute({ getParentRoute: () => rootRoute, path: 'item/$id', component: Item });

const router = createRouter({ routeTree: rootRoute.addChildren([indexRoute, itemRoute]) });

createRoot(document.getElementById('app')!).render(() => <RouterProvider router={router} />);

function RootLayout() @{
  <div>
    <Link to="/">{'Home'}</Link>
    <Outlet />
  </div>
}

function Item() @{
  const { id } = useParams({ strict: false });
  <h1>{('Item ' + id) as string}</h1>
}

The same code works in React-style .tsx too (className, return <jsx>, <Link>Home</Link>) — see tests/_fixtures/basic-react.tsx.

How it works

@tanstack/router-core keeps the router state in a reactive store. On the client, createRouter supplies the store factory (createAtom/batch from @tanstack/store — framework-agnostic), whose atoms expose .subscribe/.get. useStore binds those to octane's useSyncExternalStore, and every read hook (useRouterState, useLocation, useParams, …) is a selector over it. The match tree renders pull-based: RouterProvider renders the first match, and each route component's <Outlet/> looks up the next match via matchContext — so navigation re-renders only the matches that changed.

Headless routing

Use @octanejs/tanstack-router/core when an application owns its render tree and only needs matching, loading, and navigation. It exposes Octane's router adapter, the framework-neutral @tanstack/router-core APIs, and history constructors without importing route components or hooks.

Browser and hash histories created by this package preserve existing application fields in History API state objects and Navigation API entry state during initialization.

Same-route search navigation

Same-route search navigations now render store updates urgently. If the route component suspends on the new search input, its pending fallback can replace the current content; the previous automatic same-route hold no longer applies. To retain stale content while the new data loads, use useDeferredValue from octane on the search input (for example, page) and render the suspending content from that deferred value. The router location still advances when navigation commits.

Scope

Included: createRouter, createRootRoute, createRoute, RouterProvider, Outlet, Link, Navigate, useRouter, useRouterState, useLocation, useParams, useSearch, useLoaderData, useMatches, useNavigate, ScrollRestoration, Await/useAwaited + defer (streaming deferred data), and lazyRouteComponent (lazy/code-split route components), and not-found rendering (notFoundComponent/defaultNotFoundComponent/notFoundMode — an unknown URL or a loader throwing notFound() renders the not-found UI inside the layout, per TanStack's fuzzy/root boundary rules), plus the full @tanstack/router-core re-export (redirect, notFound, history, search helpers, types). The typed route factories, route-bound hooks, and Link surface preserve TanStack Router's registered-route inference.

The 2026-07-06 gap-closure sweep (see docs/tanstack-parity-audit.md) additionally landed the full Match pipeline (per-route Suspense/CatchBoundary/CatchNotFound, pending/error/redirected/notFound statuses), router lifecycle events, useBlocker/ Block, the complete read-hook family (useMatch, useRouteContext, useLoaderDeps, useParentMatches/useChildMatches, …), getRouteApi/ createRouteMask, useMatchRoute/MatchRoute, ClientOnly, search-param validation/middleware, and full Link parity (preloading, masking, activeProps/inactiveProps) — differential-verified byte-equal against the real @tanstack/react-router.

File routing is supported through createFileRoute and createLazyFileRoute. @octanejs/tanstack-start consumes the exported @octanejs/tanstack-router/generator-plugin from its package-owned generator; the integration masks native .tsrx template bodies without changing source offsets, so generated route-tree edits preserve authored Octane route modules.

The @octanejs/tanstack-router/ssr/server and /ssr/client entries provide RouterServer, RouterClient, buffered rendering, and readable-stream rendering. Route-owned Html/Head/Body, HeadContent, Scripts, and ScriptOnce preserve full-document SSR, head assets, and hydration. Streaming uses Octane's native injection source, so the document begins with a doctype, renderer styles are placed inside <head> with the configured CSP nonce, and serialized router data can stream without a byte-level HTML transform. These entries are the router foundation used by @octanejs/tanstack-start.

// streaming a deferred loader value
<Await promise={data.slow} fallback={'loading…'}>
  {(value) => <pre>{JSON.stringify(value) as string}</pre>}
</Await>

// scroll restoration: either the component or createRouter({ scrollRestoration: true })
<ScrollRestoration />

// code-split a route's component
createRoute({ path: 'item/$id', component: lazyRouteComponent(() => import('./Item')) })

Current scope, divergences, and verification status are tracked in the generated bindings status table (sourced from this package's status.json).

Divergences from @tanstack/react-router

  • Refs are props (octane's model) — createLink's forwardRef becomes a ref prop.
  • Native DOM events — link callbacks receive browser events rather than React synthetic events.
  • Router devtools are distributed separately and are not part of this binding.

Compiled route trees

Use @octanejs/tanstack-router/compiled for a tree whose selected route, shell, pending, error and not-found components are compiled Octane @{ ... } bodies. The entry exports RouterProvider, Outlet, and lazyRouteComponent; the existing CompiledRouterProvider, CompiledOutlet, and lazyCompiledRouteComponent names remain aliases. TanStack owns matching, loading, navigation, history and render acknowledgements.

PreconfiguredRouterProvider accepts only { router } and owns the provider, match projection, and failure presentation in Strong mode. The compiled error boundary owns synchronous route error reporting and not-found propagation. Configure the router before rendering it. Use RouterProvider when provider option props need to reconfigure the router synchronously.

import {
  createRootRoute, createRoute, createRouter, createBrowserHistory,
  RouterProvider, Outlet, lazyRouteComponent,
} from '@octanejs/tanstack-router/compiled';

const root = createRootRoute({ component: Layout });
const item = createRoute({
  getParentRoute: () => root,
  path: 'items/$id',
  component: lazyRouteComponent(() => import('./Item')),
});
const router = createRouter({
  routeTree: root.addChildren([item]),
  history: createBrowserHistory(),
});

function Layout() @{
  <main><Outlet /></main>
}
function App() @{
  <RouterProvider router={router} />
}

Import read/navigation hooks (useMatch, useParams, useSearch, useLoaderData, useLoaderDeps, useRouteContext, useLocation, useMatches, useParentMatches, useChildMatches, useNavigate, useCanGoBack, useRouter, and useRouterState) from the same /compiled entry. It owns separate non-callable contexts. Its createRoute, createRootRoute, createRootRouteWithContext, getRouteApi, createFileRoute, createLazyRoute, and createLazyFileRoute attach accessors to those contexts. Route-bound hooks and Route.Link work alongside the exported Link, useLinkProps, and createLink. Factories construct router-core base instances; they do not promise instanceof Route from the ordinary entry.

The compiled entry also exports Html, Head, Body, HeadContent, Scripts, Asset, useTags, getAssetKeys, ClientOnly, and useHydrated for document shells. Root shells can read their root match and context. SSR and client builds must select the same entry for a tree. Start applications select this entry with compiled: true in their Start plugin options, including generated route and split-module imports.

Compiled selection removes descriptor creation from route selection, bound links and document shells, but does not make the entire binding Strong or remove the generic renderer. Link retains synchronous flushSync so its transition cue is already visible in onBeforeNavigate callbacks. Both entries share a Strong keyed script template. Newly mounted route scripts execute once; hydration adopts existing scripts without replaying them, and self-removing transport scripts leave no item markers behind. Managed keys, nonces and route ownership are retained. Scroll-restoration scripts use a server-owned static hydration range, which is removed after hydration and omitted on client-only mounts. Error propagation and lifecycle dependencies retain their existing contracts.

Wrap and InnerWrap remain unsupported and produce an explicit diagnostic. For returned JSX, host strings, renderable wrappers or mixed route trees, retain the ordinary entry. Its public APIs and contexts remain available unchanged.