npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@salla.sa/twilight-theme-engine

v1.0.33

Published

Core engine for Salla React themes - component registry, hooks, contexts, routes, and base components

Readme

@salla.sa/twilight-theme-engine

The SDK behind Salla React themes. It ships the storefront routes, components, hooks, API clients, and the Vite plugin that a theme composes — so a theme is a small app, not a fork of a storefront.

Built for TanStack Start, server-rendered at the edge.

Installation

You normally don't install this yourself — salla theme create --type react scaffolds a theme that already depends on it.

pnpm add @salla.sa/twilight-theme-engine

Peer dependencies are pinned to tilde ranges (~1.166.2 for @tanstack/*, vite ^8.1.5, react ^19.2.4). The engine's dist references @tanstack/react-start internals, so drifting outside the tested minor breaks SSR at runtime. Let the install warn you rather than forcing a resolution.

What's in it

| Area | What it gives you | | -------------- | ---------------------------------------------------------------------------- | | Routes | Ready-made storefront pages — product, cart, category, blog, /account/*, … | | Components | Header/Footer, product, cart, home blocks, modal/drawer/dropdown primitives | | Hooks | useStore, useTheme, useUser, useMoney, useAsset, useWishlist, … | | Registry | Swap any registered component by name | | Hook slots | Inject content at defined extension points | | API | Typed clients for the Salla store API, for use in SSR loaders | | i18n | react-i18next integration, Arabic/RTL first | | Tooling | Vite plugin (route generation, virtual modules), ESLint config, SSR runtime |

Documentation

Theme-facing guides live in docs/ at the repo root — start with the Getting Started track or the Architecture Overview.

Engine-internal deep dives are in docs/ here:

| Document | Topic | | --------------------------------------------------------------- | -------------------------------------------------- | | HOME_COMPONENTS.md | Home component system and the data prop contract | | usePageConfig.md | Unified page configuration | | useLocation.md | Location and query-param handling | | store-identity.md | Which store a request is for, and why not config | | router-context.md | Request-scoped context across SSR and CSR | | route-ids.md | Route identifiers | | route-pagination.md | Pagination in loaders | | ProductCard.md | The ProductCard contract | | gtm-integration.md | Google Tag Manager | | i18n-instance-lifecycle.md | How the i18n instance is created and shared | | type-improvement-guide.md | Typing conventions |

Subsystem notes: src/api/README.md, src/vite/README.md, tests/README.md.

Usage

Wiring the engine into a theme

Two files do it. app/start.ts installs the request middleware:

import { createStart } from '@tanstack/react-start';
import { twilightMiddleware, earlyHintsMiddleware } from '@salla.sa/twilight-theme-engine/tanstack';

export const startInstance = createStart(() => {
  return {
    requestMiddleware: [twilightMiddleware(), earlyHintsMiddleware()],
  };
});

app/routes/__root.tsx builds the root route and renders the provider:

import { TwilightProvider } from '@salla.sa/twilight-theme-engine';
import { createTwilightRootRoute } from '@salla.sa/twilight-theme-engine/tanstack';
import themeTranslations from 'virtual:twilight/theme-translations';

export const Route = createTwilightRootRoute()({ shellComponent: RootComponent });

<TwilightProvider> takes the bundled translations (plus optional debug, skeleton, onReady, onError). Store, locale, and user are not props — the middleware resolves them per request.

Component registry

import { registry, Component } from '@salla.sa/twilight-theme-engine';

registry.register('product:card', ProductCard);
registry.override('product:card', MyProductCard);

<Component name="product:card" product={product} />;

Keys are colon-separated, and the engine only resolves three of them: product:card, the home:* blocks (via registerHomeComponents()), and account:layout-pending. Overriding any other name is a silent no-op — everything else is imported directly.

Hook slots

import { defineHooks } from '@salla.sa/twilight-theme-engine/hooks/HookRegistry';
import { HookSlot } from '@salla.sa/twilight-theme-engine/hooks/HookSlot';

defineHooks({
  'body:start': () => <AnnouncementBar />,
  'product:form.end': ({ product }) => <TrustBadges product={product} />,
});

<HookSlot name="product:form.end" context={{ product }} />;

Store, theme, and user

import { useStore } from '@salla.sa/twilight-theme-engine/hooks/useStore';
import { useTheme } from '@salla.sa/twilight-theme-engine/hooks/useTheme';
import { useTwilight } from '@salla.sa/twilight-theme-engine';

function MyComponent() {
  const store = useStore();
  const { color, font, settings, isRTL } = useTheme();
  const { user } = useTwilight();

  const isDark = settings?.footer_is_dark ?? false;
}

Settings are a plain object — read them with ?. and your own default. There is no settings.get().

Translations

import { useTranslation } from '@salla.sa/twilight-theme-engine/i18n';

function MyComponent() {
  const { t, locale, isRTL } = useTranslation();

  return (
    <>
      <h1>{t('pages.cart.title')}</h1>
      <p>{t('pages.products.sold_times', { count: 5 })}</p>
    </>
  );
}

useTranslation() returns react-i18next's t, i18n, and ready, plus locale info (locale, direction, isRTL, isLTR, languageName). The /i18n subpath also exports I18nProvider, FALLBACK_LOCALE, SUPPORTED_LOCALES, RTL_LOCALES, isLocale, and getLanguageInfo.

Money and assets

import { useMoney } from '@salla.sa/twilight-theme-engine/hooks/useMoney';
import { useAsset } from '@salla.sa/twilight-theme-engine/hooks/useAsset';

function ProductPrice({ product }) {
  const { format } = useMoney(); // also: parse, isValid
  const { cdn } = useAsset(); // also: asset, isPlaceholder

  return (
    <div>
      <img src={cdn(product.image.url, 400)} alt="" loading="lazy" />
      <p>{format(product.price)}</p>
    </div>
  );
}

format() returns a ReactNode (not a string) — it renders the currency symbol as markup.

Import paths

The engine has ~90 subpath exports. Import from the narrowest one so bundles stay small:

// Core — provider, registry, context
import {
  TwilightProvider,
  useTwilight,
  registry,
  Component,
} from '@salla.sa/twilight-theme-engine';

// Hooks — one subpath each
import { useStore } from '@salla.sa/twilight-theme-engine/hooks/useStore';
import { useMoney } from '@salla.sa/twilight-theme-engine/hooks/useMoney';

// i18n
import { useTranslation, I18nProvider } from '@salla.sa/twilight-theme-engine/i18n';

// Components — grouped by domain
import { Link } from '@salla.sa/twilight-theme-engine/common';
import { Header, Footer } from '@salla.sa/twilight-theme-engine/layout';
import { CartItem, CartSummary } from '@salla.sa/twilight-theme-engine/cart';
import { ProductCard } from '@salla.sa/twilight-theme-engine/product';

// Routes
import { Home, DefaultHomeComponents } from '@salla.sa/twilight-theme-engine/routes/home';

// API clients — for loaders
import { store } from '@salla.sa/twilight-theme-engine/api/store';

// TanStack integration
import { createRouter, createTwilightRootRoute } from '@salla.sa/twilight-theme-engine/tanstack';

// Types
import type { Store, Theme, Product } from '@salla.sa/twilight-theme-engine/types';

The authoritative list is the exports field of package.json. Never deep-import into dist/ — those paths aren't part of the public API.

Development

From the repo root:

pnpm --filter @salla.sa/twilight-theme-engine build          # tsup + tsc + dts stubs
pnpm --filter @salla.sa/twilight-theme-engine test
pnpm --filter @salla.sa/twilight-theme-engine test:coverage
pnpm --filter @salla.sa/twilight-theme-engine typecheck

| Command | Description | | ---------------- | ---------------------------------------------------------------- | | pnpm build | Bundle, add JS extensions, emit declarations and ambient .d.ts | | pnpm dev | tsup --watch | | pnpm test | Vitest (test:watch, test:coverage, test:ci) | | pnpm typecheck | tsc --noEmit | | pnpm clean | Remove dist/ |

The themes consume dist/, so build the engine before running or type-checking a theme.

Adding a new public export means adding a subpath to exports — otherwise consumers can't reach it. Verify a change survives publishing by installing Tania standalone (why).

Testing conventions, including the SSR-vs-browser split, are in tests/README.md.

License

MIT