Loading data

Fetch store data in a route loader, and reuse it in components with TanStack Query.

Read this when a page needs store data such as products, categories or brands. The engine gives you two tools: loaders that run before the page renders, and API modules that know how to talk to the store.

What SSR means here

SSR (server-side rendering) means the server builds the page's HTML before the browser gets it. Data fetched in a route loader is part of that HTML, so customers and search engines see content on the first paint, with no spinner. Data fetched later in the browser is not.

Default to a loader for anything the page needs to show. Use the browser only for things that change from user action, like the cart. See Salla SDK and events.

A loader with an API module

A loader returns the data the page component renders. It runs on the server for the first load, and in the browser when the customer navigates inside the site. The engine's API modules are separate imports under @salla.sa/twilight-theme-engine/api/*, so you only bundle what you use.

app/routes/our-brands.tsx
import { createFileRoute } from '@tanstack/react-router';
import { brand } from '@salla.sa/twilight-theme-engine/api/brands';

export const Route = createFileRoute('/{-$locale}/our-brands')({
  loader: () => brand.list(),
  component: OurBrands,
});

function OurBrands() {
  // brand.list() returns { items, next }: one page of brands and a cursor for the next page
  const { items } = Route.useLoaderData();
  return (
    <ul>
      {items.map((b) => (
        <li key={b.id}>
          <img src={b.logo} alt={b.name} width="200" height="150" />
        </li>
      ))}
    </ul>
  );
}

Add route('/our-brands', 'our-brands.tsx') to app/routes.ts, as shown in Routes and pages.

Here is a real loader from the starter theme. It uses the page API and throws a 404 if the page is missing:

app/routes/page-single.tsx
import { createFileRoute } from '@tanstack/react-router';
import { page as pageApi } from '@salla.sa/twilight-theme-engine/api/page';

export const Route = createFileRoute('/{-$locale}/$slug/page-{$id}')({
  loader: async ({ params }) => {
    const data = await pageApi.findOrThrow(params.id);
    return { page: { id: data.id, title: data.name, content: data.content } };
  },
  // …
});

Reading API modules

Each module exports one object, such as product, category, brand, page, cart, menu or store. Common names repeat across modules:

  • find(id) reads one record. list(...) reads many.
  • findOrThrow(id) does the same but throws, so a missing record becomes a 404 page. Prefer it in loaders. It exists on product, category, brand, page, blog and order.
  • queries holds ready-made TanStack Query options for the same calls.

Method names are not identical everywhere (cart.get takes a cart id, product.list takes a source), so check the API tab before you call one.

Using the same data in a component

TanStack Query is the library that caches fetched data. The engine creates one query client for loaders and components, so a component using queries shares the cache instead of fetching again. This real component from the starter theme loads a product slider after the page has rendered:

app/components/home/ProductsSlider.tsx
import { useQuery } from '@tanstack/react-query';
import { product, type ProductsListSource } from '@salla.sa/twilight-theme-engine/api/product';

function ProductsSlider({ source, sourceValue }: { source?: string; sourceValue?: number[] }) {
  const { data: result, isSuccess } = useQuery({
    ...product.queries.list({ source: source as ProductsListSource, sourceValue }),
    enabled: !!source,
  });
  if (!isSuccess) return null;
  return (
    <ul>
      {result.items.map((p) => (
        <li key={p.id}>{p.name}</li>
      ))}
    </ul>
  );
}

useQuery loads in the browser. Use useSuspenseQuery instead when the data should also be in the server HTML.

Which one to use

You needUse
Data the page cannot render withoutRoute loader + API module
Extra data for one block, after paintuseQuery with module.queries
Data that changes on a click (cart, wishlist)Salla SDK

Next: Layout and components, to change how pages look.

On this page