0

I'm currently implementing a plugin system in my application using single-spa and file-based tanstack routing.

One of this issue I'm facing is that I need to share routing context between my app and my plugin. For now my app is using a HistoryRouter and my plugin a MemoryRouter as to not collide with the browser's history API.

Here's my plugin declaration :

const lifecycles = singleSpaReact({
 React,
 ReactDOMClient,
 rootComponent: (
 /**
 * Parameters received from single-spa parent application
 * @param hostStore - The global Zustand store provided by the parent application
 * @param routingContext - The host tanstack routing context
 */
 {
 hostStore,
 hostRouter,
 implementRoutes,
 }: {
 hostStore: UseBoundStore<StoreApi<ConsoleZustandStore>>;
 hostRouter: AnyRouter;
 implementRoutes: (routes: string[]) => void;
 }
 ) => {
 // Initialize global state management with the provided store
 initGlobalStore(hostStore);
 // const pluginRoute = {path: "/mypluginroute", component: <p>hello mypluginroute</p>}
 //const newTree = hostRouter.options.routeTree.addChildren(pluginRoute)
 // hostRouter.update(newTree);
 // implementRoutes(["routeone", "routetwo"])
 // Create application-specific router configuration
 const memoryRouter = createAppRouter();
 // Return the main application component wrapped with router
 return <RouterProvider router={memoryRouter} />;
 },
 errorBoundary(err, info) {
 // Render a user-friendly error display with technical details
 return (
 <div style={{ color: "red", padding: "20px" }}>
 <h3>There was an error!</h3>
 <details style={{ whiteSpace: "pre-wrap", fontFamily: "monospace" }}>
 <summary>View error details</summary>
 <p>{err.toString()}</p>
 <hr />
 <p>Component stack trace:</p>
 <code>{info.componentStack}</code>
 </details>
 </div>
 );
 },
});

Here's my plugin loader implementation :

 <Suspense fallback={<div>Loading...</div>}>
 <motion.div
 initial={{ opacity: 0 }}
 animate={{ opacity: 1 }}
 style={{ height: "calc(100% - 47px)" }}
 >
 <Parcel
 key={currentPlugin.url}
 config={() => import(/* @vite-ignore */ pluginUrl)}
 mountParcel={mountRootParcel}
 hostStore={useGlobalStore}
 hostRouter={router}
 implementRoutes={(routes: string[])=> {
 // TODO : dynamically setup defined routes
 console.log("routes : ", routes)
 }}
 />
 </motion.div>
 </Suspense>

I've tried passing the router as props and/or sending plugin route to the host application through a callback so that I could do a hot-update of the router but it did not work, no new route was created. I'm slowly thinking of faking the routing state, communicating the current memory internal route of the plugin to the host to be appended to the url and inversely setting up a catch all route where my plugin is located and giving the location to the plugin so that he can parse it a navigate to the correct route.

Is there any way to not cheat over the tanstack router and browser history API ?

Thanks for your guidance.

asked Oct 8, 2025 at 9:21
0

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.