> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aftersell.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Szablony niestandardowe

> Nadpisuj renderowanie dowolnego bloku Aftersell Cart własnym JSX: co zastępuje szablon, co jest dostępne w zakresie, jak go stylizować i gdzie znaleźć propsy każdego bloku.

**Szablon niestandardowy** pozwala nadpisać sposób renderowania pojedynczego bloku. Zamiast wbudowanego interfejsu bloku koszyk renderuje twój własny JSX, korzystając z tych samych danych, których blok normalnie by użył. To zdolność przekrojowa, a nie osobny blok: większość bloków udostępnia ją z karty **Code**.

Ta strona opisuje to, co dotyczy **każdego** bloku. Po propsy, które przekazuje ci konkretny blok, przejdź do [dokumentacji danego bloku](#props-for-each-block).

<div id="custom-template-vs-custom-code-block">
  ## Szablon niestandardowy vs blok Custom code
</div>

Brzmią podobnie, ale robią różne rzeczy:

* **Szablon niestandardowy** *zastępuje renderowanie istniejącego bloku* twoją własną strukturą i przekazuje ci dane tego bloku (tytuł i liczbę pozycji z Headera, sumy z Summary itd.). Nie dodaje niczego nowego; przestylizowuje jeden blok.
* Blok **[Custom code](/pl/aftersell/cart/custom-code-blocks)** *dodaje nowy blok* dowolnego HTML lub Reacta w dowolnym miejscu koszyka.

Sięgnij po szablon niestandardowy, gdy wbudowany blok jest prawie dobry, ale potrzebujesz innego układu lub struktury. Sięgnij po blok Custom code, gdy chcesz dodać coś, czego wbudowane bloki nie pokrywają.

<div id="using-a-custom-template">
  ## Korzystanie z szablonu niestandardowego
</div>

1. Zaznacz blok w edytorze i otwórz jego kartę **Code**.
2. Edytuj domyślny szablon. Szablony niestandardowe to **wyłącznie JSX** (wybór HTML-albo-JSX jest zarezerwowany dla bloku Custom code).
3. Kliknij **Compile**. Kompilacja usuwa typy i transpiluje JSX, więc wychwytuje błędy **składni**. Błędy typów nie zatrzymują kompilacji — edytor oznacza je w trakcie pisania, z tym samym IntelliSense, które autouzupełnia propsy bloku.
4. Włącz szablon, aby koszyk używał go zamiast wbudowanego renderowania.
5. **Reset to default** przywraca oryginalny szablon bloku w dowolnym momencie.

<div id="writing-a-template-with-ai">
  ## Pisanie szablonu z pomocą AI
</div>

Karta Code zawiera przycisk **Copy AI prompt** (ikona różdżki ✦). Kliknięcie kopiuje do schowka samodzielny brief, który możesz wkleić bezpośrednio do sesji czatu AI (Claude, ChatGPT lub podobne).

Prompt zawiera wszystko, czego AI potrzebuje, aby napisać prawidłowy szablon dla tego konkretnego bloku:

* Zasady kompilacji (pojedyncze wyrażenie, brak `export default`, brak importów)
* Dokładne propsy, które otrzymuje blok, zgodne z tym, co pokazuje IntelliSense edytora
* Zablokowaną sygnaturę funkcji wymuszaną przez edytor
* Zasady specyficzne dla bloku (formaty pieniężne, które handlery podłączyć, wymagania dostępności)
* Sekcję do uzupełnienia, w którą wklejasz swój bieżący szablon i opisujesz zmianę, jakiej chcesz

Po skopiowaniu otwórz sesję AI, wklej prompt, uzupełnij dwa puste miejsca na dole (twój bieżący szablon i pożądaną zmianę) i wyślij. AI zwraca kompletny szablon, który możesz wkleić z powrotem do edytora i skompilować.

<Tip>
  Wklej swój istniejący szablon w sekcję do uzupełnienia, zamiast zostawiać ją pustą. AI używa go jako punktu wyjścia, więc każda dokonana już personalizacja zostaje przeniesiona, a nie zastąpiona domyślnym szablonem.
</Tip>

<Note>
  Prompt jest specyficzny dla każdego bloku. Przycisk **Copy AI prompt** pojawia się tylko na blokach obsługujących szablony niestandardowe.
</Note>

<Tip>
  Domyślny szablon, od którego zaczynasz, to **działająca kopia wbudowanej struktury bloku**, więc zawsze masz poprawny, renderujący się punkt odniesienia do modyfikacji zamiast pustej strony. Sięgaj po **Reset to default**, gdy chcesz odzyskać ten punkt odniesienia.

  Nie zawsze jest to zgodność co do bajta. Domyślny szablon Headera renderuje też `logoUrl`, dla którego wbudowana struktura nie ma miejsca, więc włączenie tego szablonu to sposób, w jaki przesłany obraz nagłówka pojawia się po raz pierwszy.
</Tip>

<div id="what-your-template-replaces">
  ## Co zastępuje twój szablon
</div>

Szablon zastępuje renderowanie bloku **całkowicie**. Wokół twojego JSX nie pozostaje żaden wrapper, co ma konsekwencje warte poznania, zanim zaczniesz coś usuwać:

| Tracisz                            | Co to oznacza                                                                                                                                                                                             |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Element wrappera bloku             | Nic nie opakowuje twojej struktury. Padding, wyrównanie i układ, które zapewniał blok, musisz teraz dostarczyć samodzielnie.                                                                              |
| **Ustawienia karty Design bloku**  | Ustawienia designu są stosowane jako style inline na wbudowanym wrapperze, a tego wrappera już nie ma. Kolory, odstępy i zaokrąglenia ustawione w karcie Design **przestają się stosować** do tego bloku. |
| Wbudowane udogodnienia dostępności | `aria-label`e, obsługa fokusu i elementy semantyczne istnieją tylko, jeśli twój JSX je zawiera.                                                                                                           |

<Warning>
  **Karta Design to pułapka, w którą wpadają ludzie.** Gdy szablon niestandardowy jest aktywny, pola karty Design są wyłączone, a obok nagłówka „Design” pojawia się ikona ostrzeżenia. Najedź na ikonę, aby zobaczyć powód. Stylizuj blok z poziomu szablonu, [inline albo własnym CSS](#styling-a-custom-template). Pola włączają się ponownie, gdy tylko wyłączysz szablon niestandardowy.
</Warning>

Co zachowujesz: pozycję bloku w koszyku, jego przełącznik widoczności, jego ustawienia (które nadal zasilają otrzymywane propsy), panel [Custom CSS](/pl/aftersell/cart/custom-css) koszyka oraz **wbudowany szkielet ładowania**.

To ostatnie zaskakuje ludzi. Blok sprawdza, czy koszyk wciąż się ładuje, *zanim* dotrze do twojego szablonu, więc wbudowany szkielet renderuje się podczas ładowania, a twój szablon uruchamia się dopiero, gdy koszyk jest gotowy. Nie musisz budować stanu ładowania.

<div id="whats-available-inside-a-template">
  ## Co jest dostępne wewnątrz szablonu
</div>

Twój szablon to pojedynczy komponent funkcyjny. Kompiluje się z **TSX**, więc adnotacje typów są dozwolone i usuwane w czasie kompilacji. Dlatego domyślne szablony są z nimi napisane:

```jsx theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
function Header(props: HeaderProps) {
  return <div>{/* … */}</div>;
}
```

**Linia sygnatury i zamykająca klamra są zablokowane** — edytor nie pozwoli edytować żadnej z nich, a najechanie pokazuje „Locked — this line can't be edited.” Piszesz treść między nimi. **Reset to default** to jedyna rzecz, która może je zastąpić.

Co jeszcze ma znaczenie:

* **Dostajesz pięć hooków:** `useState`, `useEffect`, `useMemo`, `useRef` i `useCallback`. Plus `Fragment`, dla `<>…</>`.
* **Nie ma importów.** Nie możesz niczego `import`ować i nie ma obiektu `React` w zakresie, więc żadnego `React.useReducer`, żadnego `React.Children`. Jeśli hooka nie ma na powyższej liście, nie jest dostępny.
* **Propsy są tylko do odczytu.** Mutowanie propa nic użytecznego nie da. Aby zmienić koszyk, użyj propów-handlerów, które daje ci blok (`onClose`, `increment`, `selectPlan` itd.), zamiast pisać bezpośrednio do propsów.
* **`window` jest osiągalne**, więc szablon może wywołać [Cart SDK](/pl/aftersell/cart/sdk-overview) przez `window.aftersell.cart`, gdy potrzebuje czegoś, czego propsy bloku nie pokrywają.

<div id="conventions-across-every-block">
  ## Konwencje wspólne dla wszystkich bloków
</div>

Trzy zasady obowiązują wszędzie, a ich znajomość usuwa większość zgadywania:

* **Propsy `*Html` to wstępnie oczyszczony rich text.** Renderuj je przez `dangerouslySetInnerHTML`. Przeszły już przez sanitizer koszyka, a tokeny sprzedawcy jak `{{total_price}}` są już rozwiązane.
* **Ceny przychodzące jako `string` są już sformatowane** w formacie walutowym sklepu. Ceny jako `number` są w centach. Blok daje ci jedno albo drugie, a tabela każdego bloku mówi które.
* **`isLoading` jest zawsze `false` wewnątrz szablonu.** Blok renderuje swój wbudowany szkielet i wywołuje twój szablon dopiero po załadowaniu koszyka, więc prop jest przekazywany dla kompletności, a nie po to, żebyś na nim rozgałęział logikę.

<Note>
  Kilka bloków w pewnych stanach nie zwraca w ogóle niczego, więc twój szablon nigdy nie jest wywoływany z pustymi danymi. Szablon Rewards nigdy nie widzi pustych `milestones`, a szablon Subscription upgrade nigdy nie widzi `view` równego null. Dokumentacja każdego bloku odnotowuje, gdzie to obowiązuje, więc możesz pominąć gałąź stanu pustego.
</Note>

<div id="styling-a-custom-template">
  ## Stylizowanie szablonu niestandardowego
</div>

Domyślny szablon, od którego zaczynasz, niesie nazwy klas bloku. Sposób stylizowania twoich zmian zależy od tego, jak daleko odchodzisz od tego punktu wyjścia.

<div id="the-two-class-families">
  ### Dwie rodziny klas
</div>

Każdy element w domyślnym szablonie niesie sparowaną nazwę klasy, a pełnią one bardzo różne role:

| Rodzina           | Co robi                                                                                                                              | Pisać na nią CSS?                                                                           |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- |
| `cart-internal-*` | **Niesie wbudowaną stylizację bloku.** Każda reguła w arkuszu stylów koszyka celuje w tę rodzinę.                                    | Nie. To wewnętrzna hydraulika koszyka, a edytor Custom CSS oznacza selektory jej dotyczące. |
| `cart-external-*` | **Zaczep bez własnej stylizacji.** Nic w arkuszu stylów koszyka w nią nie celuje; istnieje po to, aby twój CSS mógł się jej chwycić. | Tak. To wspierany sposób przestylizowania bloku.                                            |

Zatem `cart-internal-header__title` sprawia, że tytuł *wygląda* jak wbudowany tytuł, a `cart-external-header__title` to uchwyt, za który masz chwycić, gdy chcesz zmienić jego wygląd.

<div id="small-changes-keep-both-classnames">
  ### Małe zmiany: zachowaj obie nazwy klas
</div>

Jeśli przestawiasz elementy, zmieniasz etykiety lub dodajesz coś wewnątrz istniejącej struktury, zostaw nazwy klas w spokoju. Zachowujesz wbudowany wygląd za darmo, a przestylizowujesz przez [Custom CSS](/pl/aftersell/cart/custom-css) celujący w zaczepy `cart-external-*`.

<div id="restructuring-drop-both-classnames">
  ### Restrukturyzacja: porzuć obie nazwy klas
</div>

Gdy zmieniasz strukturę DOM, a nie ją poprawiasz, zdejmij **obie** rodziny ze swojej struktury i użyj [własnych nazw klas](#option-1-your-own-classnames-plus-custom-css). Dla każdej jest osobny powód.

**Porzuć `cart-internal-*`, ponieważ wbudowany CSS był pisany dla wbudowanego DOM.** Zachowaj te klasy na zrestrukturyzowanej strukturze, a odziedziczysz reguły układu zakładające elementy, których już nie masz: kontenery flex oczekujące innych dzieci, odstępy między elementami, które się przesunęły, pozycjonowanie względem czegoś, co usunąłeś. Zwykle objawia się to tym, że twój własny CSS „nie działa”, bo wygrywają reguły wbudowane.

<Warning>
  **Porzuć `cart-external-*`, ponieważ to nazwa współdzielona, nie twoja.** Te nazwy klas znaczą coś konkretnego na wbudowanej strukturze, a twój Custom CSS jest pisany raz dla całego koszyka. Jeśli zrestrukturyzowany szablon je ponownie użyje, każda napisana reguła celuje zarówno w twoją strukturę, jak i wbudowaną.

  To psuje się w momencie wyłączenia szablonu niestandardowego: blok wraca do wbudowanej struktury, a twój CSS nadal w nią celuje, stylizując teraz DOM, dla którego nigdy nie był pisany. Twój własny prefiks utrzymuje oba czysto rozdzielone, więc wyłączenie szablonu jest czystym powrotem.
</Warning>

Dwa sposoby na stylizację tego, co zbudujesz:

<div id="option-1-your-own-classnames-plus-custom-css">
  #### Opcja 1: własne nazwy klas plus Custom CSS
</div>

Najlepsza do wszystkiego, co będziesz utrzymywać lub ponownie wykorzystywać. Nadaj swoim klasom prefiks, z którym nikt inny nie będzie kolidować — zwykle nazwę sklepu lub marki:

```jsx theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
function Header(props) {
  return (
    <div className="northwind-custom-header">
      <div className="northwind-custom-header__title" dangerouslySetInnerHTML={{ __html: props.title }} />
      <button type="button" className="northwind-custom-header__close" onClick={props.onClose}>
        &times;
      </button>
    </div>
  );
}
```

Następnie w edytorze koszyka wybierz **Cart settings** w lewym panelu i otwórz kartę **Custom CSS** po prawej:

```css theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
.northwind-custom-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px;
}

.northwind-custom-header__title {
  font-size: 18px;
  font-weight: 600;
}

.northwind-custom-header__close:hover {
  opacity: 0.6;
}
```

Prefiks ma większe znaczenie, niż się wydaje. Bez niego klasa jak `.header` czy `.title` ryzykuje kolizję z własnymi klasami koszyka, szablonem innej aplikacji lub przyszłym blokiem.

<div id="option-2-inline-styles">
  #### Opcja 2: style inline
</div>

Bez wycieczki do panelu CSS, a wszystko żyje w jednym miejscu:

```jsx theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
```

Dobre dla rusztowania układu i jednorazowych zmian. Jego ograniczenia to te typowe: brak `:hover` i innych pseudoklas, brak media queries i brak ponownego użycia między blokami. Sięgnij po opcję 1, gdy potrzebujesz któregokolwiek z nich.

<div id="picking-an-approach">
  ### Wybór podejścia
</div>

| Sytuacja                                    | Zrób to                                                                        |
| ------------------------------------------- | ------------------------------------------------------------------------------ |
| Ta sama struktura, inna treść lub kolejność | Zachowaj obie nazwy klas, przestylizuj przez Custom CSS na `cart-external-*`   |
| Nowa struktura, stylizacja do utrzymania    | Własne prefiksowane klasy, obie rodziny koszyka porzucone                      |
| Nowa struktura, kilka szybkich reguł układu | Style inline, obie rodziny koszyka porzucone                                   |
| Dużo niestandardowego kodu w kilku blokach  | Własne prefiksowane klasy wszędzie, aby każdy szablon dało się czysto wyłączyć |

<Note>
  Koszyk renderuje się w shadow root, więc arkusz stylów twojego motywu nie sięga do jego wnętrza. Style dla szablonu niestandardowego muszą pochodzić z własnego panelu **Custom CSS** koszyka lub ze stylów inline, nie z motywu. Zobacz [Niestandardowy CSS](/pl/aftersell/cart/custom-css).
</Note>

<div id="when-a-template-fails">
  ## Gdy szablon zawodzi
</div>

Zepsuty szablon nigdy nie psuje koszyka. Blok renderuje **nic**, a wszystko wokół niego działa dalej — co jest bezpieczne, ale łatwe do przeoczenia: objawem jest puste miejsce tam, gdzie powinien być twój blok.

| Awaria                      | Kiedy ją zobaczysz         | Gdzie się zgłasza                                                                                        |
| --------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------- |
| Błąd typu                   | W trakcie pisania          | Podkreślenie inline w edytorze. **Nie** blokuje kompilacji — kompilator usuwa typy, zamiast je sprawdzać |
| Błąd składni                | Po kliknięciu **Compile**  | W edytorze, zanim dotrze do twojego sklepu                                                               |
| Awaria podczas renderowania | W sklepie, po uruchomieniu | `console.error('[aftersell-cart] module crashed: …')`                                                    |

Ponieważ blok po cichu znika, zamiast widocznie zgłaszać błąd, zawsze sprawdzaj szablon w [podglądzie](/pl/aftersell/cart/previewing-carts) przed publikacją. Jeśli blok zniknął, najpierw otwórz konsolę przeglądarki.

Dwie rzeczy warte zabezpieczenia, bo obie zawieszają szablon, który zakłada inaczej:

* **Propsy dopuszczające null.** Wiele propsów jest `null` w normalnych warunkach (`logoUrl` bez logo, `imageUrl` bez obrazu, `variantTitle` na produkcie z jednym wariantem). Sprawdzaj przed użyciem.
* **Tablice, które mogą być puste.** `discountTags` i `discountCodes` są `[]` znacznie częściej niż nie.

<div id="limitations">
  ## Ograniczenia
</div>

* **Szablony niestandardowe to nadpisania wyświetlania.** Aby uruchamiać logikę na koszyku (subskrybować zdarzenia, dodawać pozycje, reagować na zmiany), użyj [Niestandardowych skryptów](/pl/aftersell/cart/custom-scripts) i [Cart SDK](/pl/aftersell/cart/sdk-overview).
* **Obsługuje je prawie każdy blok.** Wyjątkami są blok **[Express payments](/pl/aftersell/cart/express-payments-block)**, który hostuje własne przyciski płatności Shopify, oraz sam kontener **[Cart items](/pl/aftersell/cart/cart-items-block)**, choć wiersz **Product** wewnątrz niego szablon niestandardowy obsługuje.
* **Szablon nie może zmienić tego, co blok fundamentalnie robi.** Zmienia sposób prezentacji danych bloku, a nie dane czy zachowanie za nimi.

<div id="props-for-each-block">
  ## Propsy każdego bloku
</div>

Każdy blok przekazuje własne dane. Pełna tabela propsów, z typami i przykładem, znajduje się na stronie danego bloku:

| Blok                                                                                  | Propsy, które otrzymuje                                                                                                            |
| ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| [Header](/pl/aftersell/cart/header-block#custom-template)                             | `title`, `logoUrl`, `leftSection`, `rightSection`, `itemCount`, `onClose`, `isLoading`                                             |
| [Banner](/pl/aftersell/cart/banner-block#custom-template)                             | `text`, `shouldUseTimer`, `isTimerExpiredAndShouldHide`, `isLoading`                                                               |
| [Rewards](/pl/aftersell/cart/rewards-block#custom-template)                           | `milestones`, `rewardsMessageHtml`, `showIcons`, `isLoading`                                                                       |
| [Cart items · Product](/pl/aftersell/cart/cart-items-block#custom-template)           | 25 propsów: treść per linia, identyfikatory i kontrolki ilości                                                                     |
| [Subscription upgrade](/pl/aftersell/cart/subscription-upgrade-block#custom-template) | `view`, `selectPlan`, `onChange`, `oneTimeValue` i więcej                                                                          |
| [Summary](/pl/aftersell/cart/summary-block#custom-template)                           | `leftHtml`, `rightHtml`, `discountCodes`, `totalPrice`, `savings` i więcej                                                         |
| [Checkout button](/pl/aftersell/cart/checkout-button-block#custom-template)           | `label`, `href`, `isLoading`                                                                                                       |
| [Discount code](/pl/aftersell/cart/discount-code-block#custom-template)               | `discountCodeInput`, `placeholder`, `buttonText`, `isValidating`, `isInvalid`, `setDiscountCodeInput`, `handleSubmit`, `isLoading` |
| [Empty cart](/pl/aftersell/cart/empty-cart-block#custom-template)                     | `text`, `cta`, `href`                                                                                                              |
| [Image](/pl/aftersell/cart/image-block#custom-template)                               | `imageUrl`, `altText`, `maxHeight`, `fullWidth`                                                                                    |
| [Notes](/pl/aftersell/cart/notes-block#custom-template)                               | `titleHtml`, `placeholder`, `noteInput`, `status`, `isExpanded`, `onNoteChange`, `onNoteBlur`, `onToggle` i więcej                 |
| [Product add-on](/pl/aftersell/cart/product-add-on-block#custom-template)             | `addonTitleHtml`, `descriptionHtml`, `priceHtml`, `imageUrl`, `format`, `isEnabled`, `handleAdd`, `handleToggle` i więcej          |
| [Shipping protection](/pl/aftersell/cart/shipping-protection-block#custom-template)   | `titleHtml`, `descriptionHtml`, `priceHtml`, `imageUrl`, `format`, `isEnabled`, `handleAdd`, `handleToggle` i więcej               |
| [Upsells](/pl/aftersell/cart/upsells-block#custom-template)                           | `title`, `addButtonText`, `layout`, `upsells`, `selectVariant`, `handleAdd` oraz kontrolki karuzeli                                |

Blok [Custom code](/pl/aftersell/cart/custom-code-blocks) to jedyna powierzchnia, która **dodaje** strukturę zamiast zastępować renderowanie bloku, więc jego propsy są inne: cały koszyk plus akcja dodania do koszyka. Zobacz [Bloki Custom code → Propsy](/pl/aftersell/cart/custom-code-blocks#props).
