Skip to content
Twilight React Playground
ثيم رائدaren

@salla.sa/twilight-theme-engine/routes

typeAdvanced

A lightweight index of shared route types, locale helpers and one redirect loader; route modules themselves live only on the granular subpaths.

import { RouteModule, LoaderContext, IdParams, SlugIdParams, PageQuery, RoutePagination, NotFoundPageProps, HeadDescriptor, slugBrandRedirectLoader, FALLBACK_LOCALE, SUPPORTED_LOCALES, isLocale, getLanguageInfo, Locale } from '@salla.sa/twilight-theme-engine/routes';

In plain words

The name suggests every page lives here. None does. So that each page's code downloads only when that page is visited, the modules (Home, Cart, Blog…), their loaders and their components are exported only from their own paths, such as /routes/cart.

What remains is small: the types route modules share, the props type of every page, the locale helpers and slugBrandRedirectLoader. Prefer the granular path for any name that has one.

Signature

interface RouteModule<TData, TParams extends Record<string, string>, TProps> {
  loader: (
    ctx: { params: TParams; locale?: string },
    extend?: (data: TData, ctx: { params: TParams }) => Promise<Record<string, unknown>> | Record<string, unknown>
  ) => Promise<TData>;
  head: (ctx: TwilightContext, data: TData) => HeadDescriptor;
  Component: React.ComponentType<TProps>;
}

interface IdParams { id: string }
interface SlugIdParams { slug: string; id: string }
interface PageQuery { page: number }
interface LoaderContext { search?: { page?: string | number }; locale?: string }
interface RoutePagination { current?: number; next?: string | null; previous?: string | null }
interface NotFoundPageProps {
  title?: string; message?: string; showHomeLink?: boolean; homeLinkText?: string;
  LinkComponent?: React.ComponentType<{ to: string; className?: string; style?: React.CSSProperties; children: React.ReactNode }>;
}

// Re-exported: HeadDescriptor (/utils/head), FALLBACK_LOCALE, SUPPORTED_LOCALES, isLocale,
// getLanguageInfo, Locale (/i18n), slugBrandRedirectLoader and SlugBrandParams
// (/routes/seo-redirects), and the props and data types of every page module.

Example

app/lib/locale.ts
// Names with a granular path: import them from there, not from the barrel.
import { FALLBACK_LOCALE, isLocale } from '@salla.sa/twilight-theme-engine/i18n';
import type { HeadDescriptor } from '@salla.sa/twilight-theme-engine/utils/head';
import type { WishlistPageProps } from '@salla.sa/twilight-theme-engine/routes/account';

export function localeOf(segment: string | undefined) {
  return isLocale(segment) ? segment : FALLBACK_LOCALE;
}

export function wishlistHead(data: WishlistPageProps): HeadDescriptor {
  return { title: data.page.title, robots: 'noindex' };
}

How it behaves

  • RouteModule, LoaderContext, IdParams, SlugIdParams, PageQuery, RoutePagination and NotFoundPageProps have no other public path. They are types, so importing them adds nothing to a bundle.

  • The route modules are plain as const objects that also carry id; they are not declared with RouteModule, which describes their common shape.

  • LoaderContext is the Wishlist and Wallet loaders' argument, IdParams and SlugIdParams are the params of id and slug pages, and PageQuery is their query.

  • NotFoundPageProps types the engine's not-found page, which the router shows for notFound(); that component is not exported.

  • The generated redirect route (/{-$locale}/redirect/$type/$id) imports FALLBACK_LOCALE from this path, one reason it must stay light.

Gotchas

  • The engine's shared ESLint config (@salla.sa/twilight-theme-engine/eslint) warns on every import from this path through no-restricted-imports, import type included, saying it pulls in all route modules. Its contents no longer do, but the rule stands: for the types with no other path, expect the warning or disable it on that line.

  • docs/03-routing-system.md says all route modules are exported here and imports RouteContext from @salla.sa/twilight-theme-engine/routes/types. Neither exists: the modules are on their own subpaths, there is no /routes/types subpath and no RouteContext type.

Related

Source and docs