Strona marki
UI (komponenty z hookami i JSX) jest specyficzne dla React / Next.js. W innym frameworku weź zapytanie z zakładki Raw i napisz własny widok.
Co zbudujesz
Stronę marki (/brands/[handle]): nagłówek z logo, nazwą i opisem marki, metadane SEO oraz listę produktów tej marki — wszystko jednym zapytaniem Brand. Plus indeks marek (/brands) posortowany po liczbie produktów.
Encja Brand to strona marki pobierana po handle. Nie myl jej z filtrem ProductFilter.brand (zawężanie listy produktów) ani z facetem productFilters.brands (lista marek w sidebarze) — różnicę opisuje Zapytania — Marki.
Wymagania
- Skonfigurowany SDK i provider — Konfiguracja Next.js.
- (Opcjonalnie) ekran listy produktów — Produkty.
Krok 1 — Pobierz markę po handle
handle to slug z adresu URL (np. /brands/acme). Zapytanie Brand zwraca pola strony marki (nazwa, logo, opis, SEO) i paginowaną listę produktów marki w jednym round-tripie. To operacja raw-only (brak gotowego hooka) — wybierz kontekst renderowania:
- Raw (dowolny framework)
Brak gotowego helpera SDK dla tej operacji — użyj raw operation (działa w każdym frameworku).
// Działa w dowolnym frameworku (Vue, Svelte, vanilla JS, Node, Edge)
const QUERY = `query Brand($id: ID, $handle: String, $productsFirst: Int = 20, $productsAfter: String, $productsSortKey: ProductSortKeys = BEST_SELLING, $productsFilters: [ProductFilter!]) {
brand(id: $id, handle: $handle) {
...Brand
products(
first: $productsFirst
after: $productsAfter
sortKey: $productsSortKey
filters: $productsFilters
) {
edges {
node {
...ProductCard
}
cursor
}
nodes {
...ProductCard
}
pageInfo {
...PageInfo
}
totalCount
}
}
}
fragment Brand on Brand {
id
handle
name
description
logo {
...ImageCard
}
seo {
title
description
}
productCount
}
fragment ImageCard on Image {
id
url(transform: {maxWidth: 800})
altText
width
height
thumbhash
}
fragment PageInfo on PageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
fragment ProductCard on Product {
id
handle
title
vendor
categories {
id
handle
name
}
isAvailable
averageRating
reviewCount
tags
featuredImage {
...ImageCard
}
priceRange {
minVariantPrice {
...Money
}
maxVariantPrice {
...Money
}
}
compareAtPriceRange {
minVariantPrice {
...Money
}
maxVariantPrice {
...Money
}
}
priceReduction {
...PriceReduction
}
}
fragment Money on Money {
amount
currencyCode
}
fragment PriceReduction on PriceReductionInfo {
lowestPriorPrice {
...Money
}
reductionStartedAt
referenceWindowStartedAt
referenceWindowTruncated
percentageOffLowestPrior
label
}`;
const res = await fetch(`${apiUrl}/storefront/graphql`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: QUERY, variables: { id: '…', handle: '…' }, }),
});
const { data } = await res.json();
Zwraca null, gdy marka nie istnieje lub została zarchiwizowana — obsłuż to jako 404.
Krok 2 — Rozpakuj dane marki i produkty
Pole products na marce to Relay Connection, nie tablica — wyciągnij listę przez .nodes (skrót równoważny edges.map(e => e.node)):
const brand = data.brand;
if (!brand) notFound(); // marka nie istnieje / zarchiwizowana
const items = brand.products.nodes; // produkty marki
const { hasNextPage, endCursor } = brand.products.pageInfo; // paginacja „pokaż więcej"
const total = brand.products.totalCount; // łączna liczba produktów marki
brand.products to Relay Connection — indeksowanie ([0]) zwróci undefined. Zawsze rozpakuj przez .nodes (lub .edges). Listę produktów stronicujesz przez zmienne $productsFirst / $productsAfter, a kolejność przez $productsSortKey.
Krok 3 — Widok strony marki (React)
Nagłówek marki (logo + nazwa + opis) i siatka jej produktów. <title> i meta description bierzesz wprost z brand.seo — pole jest gotowe do renderu, bo pusty meta tytuł zastępuje nazwa marki, a pusty meta opis jej opis (jako czysty tekst, przycięty do długości pokazywanej w wynikach):
function BrandPage({ brand }) {
const items = brand.products.nodes;
return (
<div>
<header>
{brand.logo && (
<img src={brand.logo.url} alt={brand.logo.altText ?? brand.name} />
)}
<h1>{brand.name}</h1>
<span>{brand.productCount} produktów</span>
{brand.description && (
<div dangerouslySetInnerHTML={{ __html: brand.description }} />
)}
</header>
<div className="grid grid-cols-4 gap-4">
{items.map((item) => (
<a key={item.id} href={`/products/${item.handle}`}>
{item.title}
</a>
))}
</div>
</div>
);
}
Uwaga: brand.description to HTML — wstaw przez dangerouslySetInnerHTML (lub wcześniej sanityzuj). Pole productCount to gotowy licznik do etykiety „N produktów" — nie musisz liczyć po stronie klienta.
Krok 4 — Indeks marek posortowany po liczbie produktów
Lista aktywnych marek (/brands) to operacja Brands — również raw-only:
- Raw (dowolny framework)
Brak gotowego helpera SDK dla tej operacji — użyj raw operation (działa w każdym frameworku).
// Działa w dowolnym frameworku (Vue, Svelte, vanilla JS, Node, Edge)
const QUERY = `query Brands($first: Int = 20, $after: String, $query: String, $sortKey: BrandSortKeys = NAME, $reverse: Boolean = false) {
brands(
first: $first
after: $after
query: $query
sortKey: $sortKey
reverse: $reverse
) {
edges {
node {
...Brand
}
cursor
}
pageInfo {
...PageInfo
}
totalCount
}
}
fragment Brand on Brand {
id
handle
name
description
logo {
...ImageCard
}
seo {
title
description
}
productCount
}
fragment ImageCard on Image {
id
url(transform: {maxWidth: 800})
altText
width
height
thumbhash
}
fragment PageInfo on PageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}`;
const res = await fetch(`${apiUrl}/storefront/graphql`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: QUERY, variables: { first: 20, after: '…' }, }),
});
const { data } = await res.json();
Aby pokazać najpopularniejsze marki najpierw, posortuj po PRODUCT_COUNT malejąco:
{ "first": 12, "sortKey": "PRODUCT_COUNT", "reverse": true }
sortKey przyjmuje NAME (domyślny), PRODUCT_COUNT, CREATED_AT, UPDATED_AT; reverse: true odwraca kolejność. Opcjonalny query filtruje po nazwie marki (np. pole „Szukaj marki").
Alternatywa — produkty marki przez filtr
Jeśli masz już ekran listy produktów (z paginacją i facetami), nie musisz pobierać produktów przez Brand.products — zawęź istniejące zapytanie products filtrem marki po handle:
query ProductsByBrand($handle: String!) {
products(first: 20, filters: [{ brand: { handle: $handle } }]) {
nodes { id title handle }
pageInfo { hasNextPage endCursor }
}
}
Kiedy co wybrać: Brand.products — gdy budujesz stronę marki i w tym samym zapytaniu potrzebujesz nazwy / logo / opisu / SEO marki. Filtr products(filters:) — gdy reużywasz istniejącego ekranu listy z sidebarem facetów. Filtr i facet opisuje Produkty — filtrowanie po marce.
Typy
Renderowane ze schematu GraphQL — nigdy nie rozjeżdżają się z API:
Brand
Canonical product brand entity (e.g. Funko, Nike). Distinct from ShopBrand (shop branding metadata).
| Pole | Typ | Opis |
|---|---|---|
description | String | Brand description. Returns ready-to-render HTML by default; pass `format: TEXT` for plain text or `format: JSON` for the structured document. Null when the merchant has not provided one. |
handle | String! | URL-friendly handle (e.g. "funko" → /brands/funko). Stable per-shop. |
id | ID! | Brand unique identifier |
logo | Image | Brand logo image. Use `logo { url(transform: { maxWidth: 200 }) altText }` for responsive sizing on brand tiles, breadcrumbs, and brand landing pages. |
name | String! | Brand display name (e.g. "Funko", "Nintendo") |
productCount | Int! | Number of active products carrying this brand. Use for "N products" labels on brand tiles. |
products | ProductConnection! | Products carrying this brand |
seo | SEO | SEO metadata (title, description) for the brand landing page. Null when not configured. |
updatedAt | DateTime! | Last update date (ISO 8601). |
BrandConnection
Paginated brand connection
| Pole | Typ | Opis |
|---|---|---|
edges | [BrandEdge!]! | Brand edges |
pageInfo | PageInfo! | Pagination info |
totalCount | Int! | Total count of brands |
Powiązane
- Encja, filtr i facet marki w API — Zapytania — Marki.
- Filtrowanie listy produktów po marce — Produkty.
- Operacje raw-only marek (SDK) — Więcej funkcji — Marki.
- Kontrakt operacji
Brand/Brands— Referencja GraphQL API.