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

LayoutProps, ComponentType and other helpers

typeAdvanced

Small helper types: a custom layout's props, the components the registry stores, loosely shaped JSON data, and nested translations.

import { LayoutProps, ComponentType, ComponentDefinition, JsonObject, TranslationMap } from '@salla.sa/twilight-theme-engine/types';

In plain words

Helpers the other types build on. LayoutProps is { children }, the props of a layout component you pass to TwilightProvider. ComponentType and ComponentDefinition describe the components the engine's registry stores. JsonObject stands for loosely shaped API data, and TranslationMap for nested translation strings.

Signature

interface LayoutProps { children: ReactNode }

type ComponentType<P = Record<string, unknown>> = React.ComponentType<P>;

interface ComponentDefinition {
  component: ComponentType;
  displayName?: string;
  original?: ComponentDefinition;   // what an override replaced
}

type JsonObject = { [key: string]: {} };   // any non-null value

interface TranslationMap { [key: string]: string | TranslationMap }

Example

app/components/layout/ThemeLayout.tsx
import type { LayoutProps } from '@salla.sa/twilight-theme-engine/types';
import { MasterLayout } from '@salla.sa/twilight-theme-engine/components/layout';

/** Wraps every page: the engine header and footer, plus a shell of the theme's own. */
export function ThemeLayout({ children }: LayoutProps) {
  return (
    <MasterLayout>
      <div className="theme-shell">{children}</div>
    </MasterLayout>
  );
}

// app/routes/__root.tsx: <TwilightProvider layout={ThemeLayout}>…</TwilightProvider>

How it behaves

  • TwilightProvider renders layout (typed ComponentType<LayoutProps> | false, default MasterLayout) around every page. @salla.sa/twilight-theme-engine/components/layout exports an identical LayoutProps.

  • ComponentType is React's ComponentType with a different default: Record<string, unknown> props instead of {}.

  • No public function returns a ComponentDefinition: the registry keeps them in a private map, and registry.resolve(name) and registry.getOriginal(name) hand back the components inside.

  • JsonObject types Product.digital_files_settings, Product.preorder, CartItem.donation, NotifyAvailability.subscribed_options and HomeComponentData.component. Its values are {} on purpose, to fit TanStack Router's type inference.

  • Nothing in the engine uses TranslationMap: the translations prop of TwilightProvider is typed TranslationMessages (Record<string, Record<string, string>>, from @salla.sa/twilight-theme-engine/i18n).

Gotchas

  • registerComponents and overrideComponents take Record<string, ComponentType>, and a component with a required prop does not fit: registerComponents({ "product:card": Card }) with Card({ product }: { product: Product }) fails with TS2322. registry.register and defineComponent are generic and accept it.

  • A JsonObject value is typed {}: const count: number = data.count fails, and { a: null } is rejected even though the API does send null in such fields (preorder arrives as null in details and [] on list cards, demo store). Narrow with typeof before use.

Related

Source and docs