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?
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();
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)
