Routing and Studio embedding

Download all docs

Use browser history in your app and preserve deep links inside Studio.

Initialize connectHostedApp() from @octonodes/ui-extensions/app once in a browser effect and dispose it when the app unmounts. It works with browser history, React Router and other routers that use pushState, replaceState and popstate.

import { connectHostedApp } from "@octonodes/ui-extensions/app";

const app = connectHostedApp();
const unsubscribe = app.subscribe(() => {
  const { status, path } = app.getSnapshot();
  // Update your page state when the session or route changes.
});
app.navigate("/products?sort=name");
// After getSnapshot().status is ready:
const response = await app.fetch("/api/products");
// On unmount:
unsubscribe();
app.dispose();

The route is /products?sort=name on your own app host. Studio mirrors it into ?appPath=%2Fproducts%3Fsort%3Dname on /studio/apps/<installation-id> or the development route. Studio preserves its own workspace parameters. Refresh, copied links, and back/forward navigation restore the app path. The app never navigates the Studio shell directly.

navigate(path, { replace: true }) replaces history. Routes must start with /; external URLs and protocol-relative paths are rejected. Ordinary links that reload a document must be supported by your web server. The generated Vite server falls back to its index for HTML navigation while preserving API/asset 404 responses. Next exports must provide the pages they intend to serve.

Trusted handoff

Studio and the hosted app exchange versioned messages bound to the expected origin, frame window and channel. The bearer is delivered only to the registered app origin. The SDK sends it only to the app's own backend and does not follow redirects. It never stores the bearer in browser storage. Non-secret embedding coordinates may survive a child refresh.

React apps can use OctonodeAppProvider from @octonodes/ui-extensions/app/react. It waits for /api/context to verify the workspace session before rendering children. Pass navigation={[{ label: "Projects", path: "/projects" }]} to show app routes in Studio's app sidebar. A direct visit without a session shows only an access message. The app's backend verifies incoming tokens with connectAppServer.

See hosting for embedding headers and sandbox behavior.