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
| Pattern | Example |
|---|---|
/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/{locale}/brands- locale = ar
- path = /brands
Route file
// @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.
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:
| Field | Type | Description |
|---|---|---|
page | Page | title is the translated "Brands". |
brands | Brand[] | 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
brandslist 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
brandsshows 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.