How to use TanStack router as an alternative to react-router?

Hi, I would like to use the tanstack router in our custom UI global page app.
Do you have any ideas on how to integrate it with the history from forge/bridge?

2 Likes

Hi @JozefBro, did you succeed with this ?
If so can you share some details ?

Hello,

I was able to implement TanStack Router on my Custom UI app the hack is to write a TanStack-compatible history adapter that wraps Forge’s view.createHistory() and implements the same interface TanStack Router expects.

forgeHistory.ts

import { createHistory } from "@tanstack/react-router";
import { view } from "@forge/bridge";
import { parseHref } from "@tanstack/history";

export async function createForgeHistory() {
  const forgeHistory = await view.createHistory();
  let blockers: any[] = [];

  // Track our own index for back/forward detection
  let index = 0;
  let prevIndex = 0;

  const getLocation = () => {
    const path = forgeHistory.location.pathname + forgeHistory.location.search;
    return parseHref(path, {
      __TSR_index: index,
      __TSR_key: forgeHistory.location.key || String(index),
    });
  };

  const history = createHistory({
    getLocation,
    getLength: () => forgeHistory.length,
    pushState: (href) => {
      prevIndex = index;
      index++;
      forgeHistory.push(href);
    },
    replaceState: (href) => {
      prevIndex = index;
      forgeHistory.replace(href);
    },
    back: () => {
      prevIndex = index;
      index = Math.max(0, index - 1);
      forgeHistory.goBack();
    },
    forward: () => {
      prevIndex = index;
      index++;
      forgeHistory.goForward();
    },
    go: (n) => {
      prevIndex = index;
      index += n;
      forgeHistory.go(n);
    },
    createHref: (href) => href,
    destroy: () => {
      unlisten();
    },
    getBlockers: () => blockers,
    setBlockers: (b) => (blockers = b),
  });

  const unlisten = forgeHistory.listen(() => {
    const delta = index - prevIndex;

    if (delta === -1) {
      history.notify({ type: "BACK" });
    } else if (delta === 1) {
      history.notify({ type: "FORWARD" });
    } else {
      history.notify({ type: "GO", index: delta });
    }

    prevIndex = index;
  });

  return history;
}

router.ts

import { routeTree } from "@/routeTree.gen";
import { createRouter, createBrowserHistory } from "@tanstack/react-router";

// Create a placeholder router for type inference
export const router = createRouter({
  routeTree,
  context: {},
  history: createBrowserHistory(), // placeholder for type safety will be updated later in main.tsx
});

// Register router type globally
declare module "@tanstack/react-router" {
  interface Register {
    router: typeof router;
  }
}

main.tsx

import { StrictMode } from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider } from "@tanstack/react-router";

import { createForgeHistory } from "./forgeHistory.ts";
import { router } from "./router.ts";

async function bootstrap() {
  // Wait for the correct history to be ready
  const history = await createForgeHistory();

  // Update the router AFTER history is ready
  router.update({
    history,
  });

  // Render only after everything is ready
  const rootElement = document.getElementById("app");
  if (rootElement && !rootElement.innerHTML) {
    const root = ReactDOM.createRoot(rootElement);
    root.render(
      <StrictMode>
          <RouterProvider router={router} />
      </StrictMode>
    );
  }
}

bootstrap();
2 Likes

I am doing something very similar, my navigation works yet I am having some error at the console upon navigating to a new route that I haven’t been to in the same instance

I am using the same forgeHistory.ts as @AymaneAitlaasri except that I change blockers from any to NavigationBlocker.

index.ts

// Create a new router instance
const router = createRouter({
    routeTree,
    context: {
        // some context
    },
    defaultPreloadStaleTime: 10_000,
    defaultPreload: 'intent',
    scrollRestoration: true,
})


// Register the router instance for type safety
declare module '@tanstack/react-router' {
    interface Register {
        router: typeof router
    }
}


function App({ history }: { history: RouterHistory }) {
    const context = useProductContext()
    if (!context) return <div>Loading...</div>

    return (
        <StrictMode>
            <QueryClientProvider client={queryClient}>
                <RouterProvider
                    router={router}
                    history={history}
                    context={{ 
                     // context
                    }}
                />
            </QueryClientProvider>
        </StrictMode>
    )
}

createForgeHistory().then((history) => {
    if (history) {
        createRoot(document.getElementById('root')!).render(
            <App history={history} />
        )
    }
})

Grateful if anyone can figure out how to debug the Invariant failed error.

package versions:
“@forge/bridge”: “^5.3.0”,
“@tanstack/react-router”: “1.132.31”,
“vite”: “^7.1.1”,
“@tanstack/router-plugin”: “1.132.31”, (in devDependencies)