Brands page

The index of every brand in the store, grouped by first letter.

Framework: TanStack Start

This page is written for TanStack Start, the default and currently supported framework. See what depends on the framework.

The customer sees every brand's logo, grouped by first letter, with a letter bar that jumps to each group. Each logo opens the brand page. Customise this route to change the title or draw the groups yourself.

URL

PatternExample
/brands/brands
/{locale}/brands/ar/brands

The only param is the optional locale (locale prefix). There are no search params.

Try it

This is the brands list as the engine renders it, running against the demo store. Switch the language or the width.

/ar/brandsOpen
Route
/{locale}/brands
This address becomes
  • locale = ar
  • path = /brands

Route file

app/routes/brands.tsx
// @auto-generated
import { createFileRoute } from '@tanstack/react-router';
import { Brands } from '@salla.sa/twilight-theme-engine/routes/brands';
import type { BrandsPageProps } from '@salla.sa/twilight-theme-engine/routes/brands';
import { withHead } from '@salla.sa/twilight-theme-engine/tanstack';

export const Route = createFileRoute('/{-$locale}/brands')({
  loader: ({ params }): Promise<BrandsPageProps> => Brands.loader({ locale: params.locale }),
  head: withHead(Brands),
  component: BrandsComponent,
});

function BrandsComponent() {
  const data: BrandsPageProps = Route.useLoaderData();
  return <Brands.Component {...data} />;
}

To take the page over, write your own file and map the path in app/routes.ts: route('/brands', 'my-brands.tsx'). See Fork a built-in route.

A customised version

This keeps the engine loader and component and adds a suffix to the title.

app/routes/my-brands.tsx
import { createFileRoute } from '@tanstack/react-router';
import { Brands } from '@salla.sa/twilight-theme-engine/routes/brands';
import type { BrandsPageProps } from '@salla.sa/twilight-theme-engine/routes/brands';
import { withHead } from '@salla.sa/twilight-theme-engine/tanstack';

export const Route = createFileRoute('/{-$locale}/brands')({
  loader: ({ params }): Promise<BrandsPageProps> => Brands.loader({ locale: params.locale }),
  head: withHead(Brands, (head) => ({ ...head, title: `${head.title} | Official store` })),
  component: BrandsComponent,
});

function BrandsComponent() {
  const data: BrandsPageProps = Route.useLoaderData();
  return <Brands.Component {...data} />;
}

Brands.loader also takes an optional second argument, extend(data), that returns fields to merge into the loader data.

What it loads

BrandsPageProps:

FieldTypeDescription
pagePagetitle is the translated "Brands".
brandsBrand[]The first page of brands (id, name, logo, url, ar_char, en_char and more).
pagination{ next?: string | null }Set while more pages exist.

The first request loads this on the server. Brands.Component then fetches the remaining pages in the browser, and groups every brand by its first letter (ar_char when the locale is ar, en_char otherwise; brands without one go under #). Groups are sorted by letter. Brands.Component is loaded lazily; render it the way the generated route does. See Brands API.

The page has two hook slots around the groups: brands:index.items.start and brands:index.items.end.

Head and SEO

Title is the translated "Brands", plus hreflang links. There is no description, canonical or Open Graph tag. See SEO and head.

Edge cases

  • No brands. An empty brands list shows an empty state with a link home. It is not a 404.
  • More than one page of brands. The letter bar and groups fill in as the browser loads the other pages. A custom page that renders only brands shows only the first page.
  • Letter bar. It lists only letters that have brands, and sticks to the top after 200px of scrolling.
  • Request failure. The error page is shown.

See also

On this page