createTwilightRootRoute
Creates the root route of app/routes/__root.tsx with the engine's store-settings loading, global head tags and "Store Unavailable" error page.
import { createTwilightRootRoute } from '@salla.sa/twilight-theme-engine/tanstack';In plain words
Every page of a theme sits inside one root route: it draws the <html> document and runs first on every navigation. createTwilightRootRoute() makes that route with three things already attached:
- a step that loads the store settings, translations and installed apps before any page, because nothing can render without the settings;
- the tags every page needs in
<head>: the Salla SDK, fonts, icons, the favicon, and the store's title and description; - the "Store is temporarily unavailable" page, shown when the store cannot be loaded.
You call it twice in a row, createTwilightRootRoute()({ … }), and usually pass only shellComponent, the component that draws the document.
Signature
function createTwilightRootRoute(): (options: any) => RootRoute
// createTwilightRootRoute()(options) is
// createRootRouteWithContext<RouterInitialContext>()({
// beforeLoad: rootBeforeLoad, // settings, translations, apps, locale, auth token
// head: rootHead, // SDK, import map, fonts, icons, base SEO tags
// errorComponent: RootErrorComponent, // "Store is temporarily unavailable"
// ...options, // yours are spread last
// })Example
import { HeadContent, Outlet, Scripts } from '@tanstack/react-router';
import { TwilightProvider } from '@salla.sa/twilight-theme-engine';
import {
createTwilightRootRoute,
getTwilightContext,
} from '@salla.sa/twilight-theme-engine/tanstack';
import themeTranslations from 'virtual:twilight/theme-translations';
import '../styles/app.css';
export const Route = createTwilightRootRoute()({
shellComponent: RootComponent,
});
function RootComponent() {
// The root beforeLoad has already written the locale for this request.
const ctx = getTwilightContext();
return (
<html lang={ctx.locale} dir={ctx.dir} suppressHydrationWarning>
<head>
<meta charSet="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<HeadContent />
</head>
<body suppressHydrationWarning>
<TwilightProvider translations={themeTranslations}>
<Outlet />
</TwilightProvider>
<Scripts />
</body>
</html>
);
}
How it behaves
The engine
beforeLoadruns on every navigation, on the server and in the browser. It takes the locale from the{-$locale}param (falling back toar), loads settings and translations through theQueryClient(cached, so a navigation rarely makes a request), loads app snippets and app settings (failures there are logged, not thrown), and writes settings, locale, i18n and the auth token into the twilight context.It returns
{ settings, locale, dir, authToken, appsSnippets, appsSettings }, which every child route receives incontextnext toqueryClient.When the settings request returns nothing it throws
SettingsError("Failed to load store settings. Make sure VITE_STORE_DOMAIN is set."), and the error page takes over. On localhost and the preview host, a store named by?storeId=or a cookie instead of the path gets a full-page redirect to/<username>/…once its username is known.The root head falls back to the title "Store Unavailable" when there are no settings. Its base tags include a
canonicallink to the origin of the store's URL (buildBaseHeadwithout a path); a page head that sets its own canonical adds a second link, because TanStack appends the links of every matched route.The error page is fixed:
<html lang="ar" dir="rtl">, English text and a Retry button that reloads, whatever the store's language.This playground's own root (packages/playground/app/routes/__root.tsx) is built the same way. The difference that matters is
layout={PlaygroundLayout}onTwilightProvider, which draws the docs chrome on playground routes and the engineMasterLayouteverywhere else.
Gotchas
Your options are spread last, so passing
beforeLoad,headorerrorComponentreplaces the engine's.rootBeforeLoadandrootHeadare not exported, so you cannot wrap them: with your ownbeforeLoadnothing writes the settings, and everygetTwilightContext().settingsread throwsSettingsError; with your ownheadthe Salla SDK script, fonts and icons disappear. PassshellComponent, andcomponentorstaticDataif you need them.The options are typed
any, so TypeScript checks nothing you pass: a misspeltshellComponetcompiles and is ignored. Compare the key with the example when the document renders without your shell.
Related
Builds the theme's TanStack router with the engine's defaults: a data cache, SSR hydration, a loading skeleton, and the error and 404 pages.
getTwilightContextReads the engine's context outside React: store settings, language, location, route id, auth token and the data cache.
TwilightProviderThe component a theme mounts once around its pages; it shares the store, theme, language and navigation with everything inside it.
SettingsErrorThe error thrown when a request has no store settings; left uncaught at the root it becomes the "Store Unavailable" page.