getTwilightContext
Reads the engine's context outside React: store settings, language, location, route id, auth token and the data cache.
import { getTwilightContext, TwilightContext, TwilightLocation } from '@salla.sa/twilight-theme-engine/tanstack';In plain words
Loaders, head functions and the root shell run where React hooks cannot be used. getTwilightContext() is how they ask "which language is this page in, what are the store's settings, where are we?".
On the server every request has its own private copy, opened by twilightMiddleware(). In the browser there is one copy for the whole page, kept up to date by the router.
It returns the values as they are now. Unlike a hook, it does not make a component render again when they change, so inside components prefer useTwilight() and the engine hooks.
Signature
function getTwilightContext(): TwilightContext
interface TwilightContext {
queryClient: QueryClient;
settings: StoreContext; // getter: throws SettingsError until loaded
locale: string; // 'ar' until the root route has run
dir: 'ltr' | 'rtl'; // from locale: ar, fa, he and ur are rtl
routeId: string; // a RouteId such as 'cart', or the raw TanStack id
location: TwilightLocation;
authToken: string | null;
scope: ActiveScope | null; // server only
scopeId: string | null; // scope?.id
storeId: string | null; // server only
storeBase: string | null; // server only
versionId: string | null; // server only
requestHost: string | null; // server only
i18n: i18n; // created on first read when missing
page?: PageContext;
appsSnippets?: AppSnippet[];
appsSettings?: AppSettings;
extras?: Record<string, unknown>;
}
interface TwilightLocation<TSearch = Record<string, unknown>> {
href: string;
pathname: string;
search: TSearch;
searchStr: string;
state: Record<string, unknown>;
hash: string;
}Try it live
getTwilightContext().Try this: pick request addressing: every field is null here, even on localhost, because only the server middleware writes them.import { getTwilightContext } from '@salla.sa/twilight-theme-engine/tanstack';
// A loader: runs on the server for the first page, in the browser after that.
export async function offersLoader() {
const { locale, location } = getTwilightContext();
return { locale, page: Number(location.search.page ?? 1) };
}
Example
// Declared in app/routes.ts: route('/faq', 'faq.tsx')
import { createFileRoute } from '@tanstack/react-router';
import { getTwilightContext } from '@salla.sa/twilight-theme-engine/tanstack';
export const Route = createFileRoute('/{-$locale}/faq')({
loader: () => {
const { settings, dir } = getTwilightContext();
return { storeName: settings.store?.name ?? '', dir };
},
component: FaqPage,
});
function FaqPage() {
const { storeName, dir } = Route.useLoaderData();
return <h1 dir={dir}>Questions about {storeName}</h1>;
}
How it behaves
Server: one context per request, kept in
AsyncLocalStorageand opened bytwilightMiddleware(). Browser: one module-level context for the page.The object it returns is a set of getters over that context, not a copy: keep it and read it later, and you get the values as they are then.
Who writes what:
twilightMiddleware()the auth token, scope, store id, store base, version id and request host;createRouter()thequeryClient; the root route'sbeforeLoadthe settings, locale, i18n, location and auth token; the router the page, route id and location on each navigation.__root.tsxreads it for<html lang dir>because the shell renders aboveTwilightProvider, whereuseTwilight()has no provider yet.Values are also on
useTwilight()inside components, andlocationonuseLocation()from/providers.
Gotchas
settingsis a getter that throwsSettingsErrorwhile no settings are loaded, soctx.settings?.storedoes not protect you: the throw happens whensettingsis read. In code that can run before the root route, such as a head function during hydration, catchSettingsErrorthe waywithHead()does.On the server, outside a request, it throws
[Twilight] No request context. Wrap with runWithTwilightContext().Never call it at the top level of a module; call it inside loaders,beforeLoad, head functions or components.In the browser
storeId,storeBase,versionId,requestHostandscopeare alwaysnull: only the server middleware writes them, and hydration copies settings, locale, i18n, auth token, location, page and route id (hydrateTwilightContextin src/tanstack/router.tsx). Read them on the server and return what the browser needs from a loader.It is not reactive. Reading it while rendering gives the value once, and a later
updateTwilightContext()does not render that component again. Inside components useuseTwilight(),useRouteId()oruseLocation().packages/theme-engine/docs/router-context.md describes
getRouteContext()and a/routersubpath. Neither exists;getTwilightContext()from/tanstackis the function to use.
Related
Writes values into the current twilight context: the request's own on the server, the page's single context in the browser.
twilightMiddlewareThe request middleware every theme lists first in app/start.ts; it opens the private, per-request context the rest of the engine reads.
useTwilightReads everything TwilightProvider knows: store, theme, settings, language, direction, current page, login token and the Salla SDK.
SettingsErrorThe error thrown when a request has no store settings; left uncaught at the root it becomes the "Store Unavailable" page.
useLocationReads the current address as the router sees it (path, parsed query, raw query, hash), in the server render and in the browser.
Source and docs
- Engine source:
packages/theme-engine/src/twilight/context.ts