Skip to content

Embed in a React app

Show a tablewalk App, or one of its lists, records or dashboards, inside your own React app’s routes, under your router.

import { TablewalkApp, TablewalkProvider } from 'tablewalk/react';
<TablewalkProvider endpoint="/api/tw">
<TablewalkApp app="compass" basePath="/crm" />
</TablewalkProvider>;

The endpoint is the tablewalk server: the handler mounted in your server with basePath: '/api/tw', or a tablewalk process your server rewrites /api/tw to. basePath is where your app shows the App: /crm/leads/127 is lead 127. Your router owns the address bar, so Back, Forward and your own links move the App too.

The general path puts this beside a route and a design of your own, in one sample App.

import { useReactRouterNavigator } from 'tablewalk/react/react-router';
function Crm() {
return (
<TablewalkProvider endpoint="/api/tw" navigator={useReactRouterNavigator()}>
<TablewalkApp app="compass" basePath="/crm" />
</TablewalkProvider>
);
}
// <Route path="/crm/*" element={<Crm />} />
import { useTanStackNavigator } from 'tablewalk/react/tanstack-router';
const crm = createRoute({ getParentRoute: () => root, path: '/crm/$', component: Crm });
function Crm() {
return (
<TablewalkProvider endpoint="/api/tw" navigator={useTanStackNavigator()}>
<TablewalkApp app="compass" basePath="/crm" />
</TablewalkProvider>
);
}
app/crm/[[...rest]]/page.tsx
'use client';
import { useNextNavigator } from 'tablewalk/react/next';
export default function Crm() {
return (
<TablewalkProvider endpoint="/api/tw" navigator={useNextNavigator()}>
<TablewalkApp app="compass" basePath="/crm" />
</TablewalkProvider>
);
}
import { routerNavigator, STATE_KEY, walkOf } from 'tablewalk/react';
const navigator = routerNavigator({
read: () => ({ pathname: router.pathname, search: router.search, state: walkOf(router.state) }),
push: (href, state) => router.push(href, { [STATE_KEY]: state }),
replace: (href, state) => router.replace(href, { ...router.state, [STATE_KEY]: state }),
});
router.subscribe(() => navigator.hostMoved()); // on every move: the App's own are told apart

Pass it as the Provider’s navigator. The App’s walk rides in the entry’s state under STATE_KEY, beside your router’s own. With no router, the Provider uses window.history (browserNavigator()).

On the server every element renders an empty slot (or its fallback); the App loads in the browser the first time an element is on screen.

<TablewalkList app="compass" view="leads" tab="working" onOpenRecord={(lead) => openDrawer(lead.key)} />
<TablewalkRecord app="compass" resource="lead" id={127} />
<TablewalkDashboard app="compass" view="sales-dashboard" period="last-30-days" />
<TablewalkPage app="depot" page="pick-walk" params={{ list: '12' }} />

A surface has no App chrome and keeps its own address, so your address bar does not change. TablewalkPage draws one of the App’s free-form pages by its path. onOpenRecord hands you the record a list opens; without it, the record opens in place. A page shows one tablewalk element at a time.

With auth left out, the App uses tablewalk’s own session cookie. To sign people in with your app’s session instead, mint an assertion on your server and hand it over:

<TablewalkProvider
endpoint="/api/tw"
auth={{ assertion: () => fetch('/my/tablewalk-assertion').then((r) => r.text()) }}
>

On the same origin the exchange sets the session cookie. On another origin the session is a token kept in memory, and is renewed before it lapses; the server must list your page’s origin (embed.origins on the handler).

Before it draws, the Provider asks the server once: the sign-in comes back with the first page’s data in it, so the App starts on one request.

<TablewalkAssistant app="compass" conversation={id} />

The App’s assistant as a page of your app, under the same Provider.

You import no CSS. tablewalk’s styles are scoped to its own elements, in one cascade layer named tablewalk, so they never reach your page. Your styles in cascade layers (a reset, Tailwind’s base) do not reach the App. Your unlayered element rules (button { … }) still do; put them in a layer. theme, mode and density on the Provider choose the look. theme is a look’s name, or your own made with defineTheme:

import { defineTheme } from 'tablewalk/app';
const harbor = defineTheme({ id: 'harbor', label: 'Harbor', extends: 'mono', colors: { light: { accent: '#0b6e4f' } } });
<TablewalkProvider endpoint="/api/tw" theme={harbor}>