Katalog /themes/ w PrestaShop: Jak dzialaja szablony, zasoby i motywy potomne

This is Part 2 of the PrestaShop Architecture Series. Part 1 covers the full folder structure overview.

Co kontroluje katalog /themes/

Every pixel of your store's front office passes through the /themes/ directory. When a customer visits a product page, PrestaShop does not render HTML from PHP directly -- it loads a Smarty template from your active theme, fills it with data from controllers and hooks, compiles it, and sends the result. The theme controls the HTML structure, the CSS styling, the JavaScript behavior, and even which hooks are displayed where.

Understanding how this directory works means understanding how PrestaShop builds every page your customers see.

Struktura katalogu motywu

themes/
  classic/                          <-- PS 1.7/8 default
  hummingbird/                      <-- PS 9 default
  my-child-theme/                   <-- your customizations
  
  [active-theme]/
    config/
      theme.yml                     <-- theme identity, assets, layouts
    templates/
      catalog/
        product.tpl                 <-- product page
        listing/
          category.tpl              <-- category listing
          product-list.tpl          <-- product grid/list
      checkout/
        cart.tpl                    <-- shopping cart
        checkout.tpl                <-- checkout flow
        order-confirmation.tpl      <-- thank-you page
      cms/
        page.tpl                    <-- CMS content pages
      customer/
        my-account.tpl              <-- customer dashboard
        addresses.tpl               <-- address management
        history.tpl                 <-- order history
      errors/
        404.tpl                     <-- page not found
        maintenance.tpl             <-- maintenance mode
      _partials/
        head.tpl                    <-- <head> section, meta, CSS
        header.tpl                  <-- site header, navigation
        footer.tpl                  <-- site footer
        notifications.tpl           <-- success/error messages
        breadcrumb.tpl              <-- breadcrumb navigation
        pagination.tpl              <-- list pagination
      layouts/
        layout-full-width.tpl       <-- full-width page layout
        layout-left-column.tpl      <-- with left sidebar
        layout-right-column.tpl     <-- with right sidebar
        layout-both-columns.tpl     <-- both sidebars
    assets/
      css/
        theme.css                   <-- main compiled stylesheet
        custom.css                  <-- user overrides (if theme supports it)
      js/
        theme.js                    <-- main compiled JavaScript
        custom.js                   <-- user scripts
      img/
        logo.png                    <-- store logo
        favicon.ico                 <-- browser tab icon
    modules/
      ps_featuredproducts/
        views/templates/hook/
          ps_featuredproducts.tpl    <-- theme override of module template
    translations/
      en-US/
        Shop/Theme/Global.xlf       <-- theme-specific translations
    preview.png                     <-- 200x200 theme preview

Jak PrestaShop rozwiazuje szablony

When a controller needs to render a page, PrestaShop searches for the template in a specific order. Understanding this order is critical -- it determines which file actually renders your page.

Template resolution order (from highest to lowest priority):

  1. Child theme -- themes/my-child/templates/catalog/product.tpl
  2. Parent theme -- themes/classic/templates/catalog/product.tpl

Module template resolution (for hooks):

  1. Child theme module override -- themes/my-child/modules/ps_featuredproducts/views/templates/hook/ps_featuredproducts.tpl
  2. Parent theme module override -- themes/classic/modules/ps_featuredproducts/views/templates/hook/ps_featuredproducts.tpl
  3. Module's own template -- modules/ps_featuredproducts/views/templates/hook/ps_featuredproducts.tpl

This three-level fallback is what makes child themes possible: you only include files you want to change, and everything else falls through to the parent. It is also what makes theme-based module overrides possible: a theme can completely redesign how a module renders without touching the module's own files.

Practical implication you must understand: When a theme overrides a module template, the override takes absolute priority. If the module releases an update that fixes a security issue in its template, your theme's override will still serve the old version. You must manually compare and update theme overrides when modules update. This is the hidden maintenance cost of theme overrides that most documentation glosses over.

Jak renderuje sie strona: Od zapytania do HTML

Here is the exact sequence when a customer visits /en/product/linear-drain-800mm:

  1. Apache/Nginx receives the request, rewrites it via .htaccess to index.php
  2. PrestaShop bootstrap loads config/config.inc.php, initializes the Dispatcher
  3. Dispatcher parses the URL, identifies the controller: ProductController
  4. ProductController::init() loads the product data from the database
  5. ProductController::initContent() assigns Smarty variables ($product, $combinations, etc.)
  6. Hook execution -- the controller calls Hook::exec('displayProductAdditionalInfo') and other hooks. Each registered module runs its hook method, which typically assigns its own Smarty variables and returns rendered HTML.
  7. Template selection -- the controller calls $this->setTemplate('catalog/product'). PrestaShop resolves this to the actual file path using the resolution order above.
  8. Layout wrapping -- the product template is rendered inside a layout template (layouts/layout-full-width.tpl). The layout includes _partials/head.tpl, _partials/header.tpl, the page content, and _partials/footer.tpl.
  9. Smarty compilation -- Smarty compiles the .tpl files into PHP, caches the result in var/cache/prod/smarty/compile/. Subsequent requests skip compilation and use the cached PHP directly.
  10. HTML output -- the compiled template executes, produces HTML, and PrestaShop sends it to the browser with CSS/JS references.

The entire chain typically takes 200-500ms on a well-configured server. Of that, template compilation is 0ms on cached requests (the compiled PHP is reused) and 50-100ms on the first request after a cache clear.

theme.yml: Plik konfiguracyjny motywu

The config/theme.yml file defines everything about your theme: its identity, parent relationship, asset registration, layout assignments, and hook configuration.

parent: classic                     <-- child theme inherits from this
name: my-child-theme                <-- directory name (must match)
display_name: My Custom Theme       <-- human-readable name
version: 1.0.0
author:
  name: "Your Name"
  email: "you@example.com"

meta:
  compatibility:
    from: 1.7.0.0
    to: ~                           <-- no upper limit

assets:
  use_parent_assets: true           <-- inherit CSS/JS from parent
  css:
    all:
      - id: custom-styles
        path: assets/css/custom.css
        media: all
        priority: 200               <-- higher = loaded later
  js:
    all:
      - id: custom-scripts
        path: assets/js/custom.js
        priority: 200

theme_settings:
  default_layout: layout-full-width
  layouts:
    category: layout-left-column
    product: layout-full-width
    checkout: layout-full-width

Critical fields:

  • parent -- establishes the inheritance chain. Without this, PrestaShop treats the theme as standalone and won't fall back to any parent templates.
  • use_parent_assets: true -- loads the parent's CSS and JS before yours. Set to false only if you want to completely replace the parent's styles (rare, and you'll need to provide everything yourself).
  • priority on assets -- controls load order. Parent assets typically have priority 100. Set your child assets to 200+ to ensure they load last and can override parent styles.
  • layouts -- maps page types to layout templates. This is how you get a sidebar on category pages but full-width on product pages.

Pipeline zasobow: Jak CSS i JS trafiaja do przegladarki

PrestaShop's asset system has multiple layers, and understanding them prevents the most common "my CSS changes don't show up" problems.

Asset registration sources (in load order):

  1. theme.yml assets -- registered in the theme configuration file
  2. Module assets -- registered via $this->context->controller->registerStylesheet() or registerJavascript() in the module's hook methods
  3. Controller-specific assets -- registered in the controller's setMedia() method

The CCC (Combine, Compress, Cache) layer:

When CCC is enabled (Back Office > Advanced Parameters > Performance), PrestaShop:

  1. Combines all CSS files into one (or a few) combined files
  2. Minifies the combined CSS
  3. Names the output with a content hash: themes/classic/assets/cache/combined-abc123.css
  4. Does the same for JavaScript

This means: after editing a CSS file, you must either increment the cache version (PS_CCCCSS_VERSION in ps_configuration) or delete the combined files in themes/*/assets/cache/. Otherwise the browser continues loading the old combined file because the filename hasn't changed.

This is the #1 cause of "I changed CSS but nothing happened" in PrestaShop. It is not a bug -- it is the caching system working as designed. The fix is always: clear CCC cache, not bypass it.

Silnik szablonow Smarty: Co musza wiedziec tworcymudolow

PrestaShop uses Smarty 3/4 for front-office templates. The key concepts:

Variable assignment: Controllers and modules assign data to Smarty, then templates render it.

// In PHP (controller or module hook):
$this->context->smarty->assign('my_data', $someArray);

// In .tpl template:
{$my_data.name}
{$my_data.price|number_format:2}

Block system: Templates define named blocks that child templates can override.

// Parent template (classic/templates/catalog/product.tpl):
{block name='product_description'}
  <div class="product-description">{$product.description nofilter}</div>
{/block}

// Child template (my-child/templates/catalog/product.tpl):
{extends file='parent:catalog/product.tpl'}
{block name='product_description'}
  <div class="custom-description">
    {$product.description_short nofilter}
    <hr>
    {$product.description nofilter}
  </div>
{/block}

The parent: prefix is mandatory in {extends}. Without it, Smarty finds the child's own file and enters an infinite loop that crashes with a timeout. This is the single most common child theme error.

The nofilter modifier: By default, Smarty auto-escapes variables to prevent XSS. For variables that contain HTML (like product descriptions), you must use {$var nofilter}. Without it, HTML tags appear as literal text on the page.

Hook rendering in templates:

{hook h='displayProductAdditionalInfo' product=$product}
{hook h='displayFooter'}
{widget name='ps_emailsubscription' hook='displayFooter'}

The {hook} tag calls Hook::exec(), which runs every module registered on that hook and concatenates their output. The {widget} tag calls a specific module's widget method regardless of hook registration.

Classic vs Hummingbird: Roznice architektoniczne

AspectClassic (PS 1.7/8)Hummingbird (PS 9)
CSS frameworkBootstrap 4.6Bootstrap 5.3
CSS architectureFlat specificityCSS Layers (@layer)
JavaScriptjQuery + vanillaTypeScript + jQuery (compat)
Build systemWebpack 4Webpack 5
CSS custom propertiesMinimalExtensive (--bs-*)
Class namingMixed BEM/BootstrapBEM-like (.product__name)
Dark modeNot supportedCSS variable-based support
Image formatJPG/PNGWebP default

The CSS Layers in Hummingbird fundamentally change how specificity works:

@layer vendors, bs-base, bs-components, bs-custom-components,
       ps-base, ps-components, ps-pages, ps-modules, utilities;

Styles in later layers override earlier ones regardless of selector specificity. A simple .product__name { color: red; } in the ps-components layer beats a highly specific #content .product-container .product__name { color: blue; } in the bs-components layer. This eliminates !important wars but requires understanding which layer your CSS belongs in.

For module developers targeting both themes: use var(--css-property, fallback-value) pattern. On Classic, CSS custom properties aren't defined, so the fallback wins. On Hummingbird, the custom property is defined and wins. One CSS file serves both themes correctly.

Podkatalog modules/: Nadpisywanie szablonow modulow na poziomie motywu

The themes/[theme]/modules/ directory is one of PrestaShop's most powerful and most dangerous features. It lets a theme completely replace how any module renders its templates.

themes/my-child/modules/
  ps_featuredproducts/
    views/templates/hook/
      ps_featuredproducts.tpl        <-- replaces the module's own template
  ps_searchbar/
    views/templates/hook/
      ps_searchbar.tpl               <-- custom search bar rendering

When to use theme module overrides:

  • The module's HTML structure doesn't fit your design
  • You need to add/remove elements from a module's output
  • You want to change the Smarty logic (show/hide based on conditions)

When NOT to use theme module overrides:

  • You want to change CSS only -- use your child theme's CSS instead
  • The module provides configuration options for what you want to change -- use those first
  • You're overriding a module you update frequently -- every update requires re-checking the override

The maintenance trap: We maintain 140+ modules. When a module updates its template to fix a bug or add a feature, stores with theme overrides of that template never see the fix. The override silently serves the old version. We see this regularly in support -- "the feature doesn't work after updating" turns out to be a stale theme override from 3 versions ago. The fix is always: diff the override against the current module template, merge changes, or delete the override entirely.

Wplyw architektury motywu na wydajnosc

Theme choices directly impact page load time:

  • Template compilation: First request after cache clear compiles all .tpl files to PHP. A theme with 50 template files takes 50-100ms longer on first load. Subsequent requests: 0ms (compiled PHP is cached).
  • Asset count: Each CSS/JS file is an HTTP request (without CCC). 15 separate CSS files = 15 requests. With CCC enabled: 1-2 requests. Always enable CCC in production.
  • Image sizes: The theme's theme.yml defines image types (home_default: 250x250, large_default: 800x800). Using oversized image types on listing pages (e.g., large_default for category thumbnails) wastes bandwidth. Use the smallest type that looks good.
  • Hook density: Each {hook} call in a template executes all registered modules. A header with 5 hooks, each with 3 modules, runs 15 module hook methods per page load. Uninstall modules you don't use -- even if they're disabled, some still register hooks.
  • Smarty nocache: Content inside {nocache}...{/nocache} blocks is never cached by Smarty and re-evaluates on every request. Use sparingly -- typically only for user-specific data like cart counts or login status.

Czeste problemy z /themes/ i rozwiazania

ProblemCauseFix
CSS changes don't appearCCC serving cached combined fileDelete themes/*/assets/cache/*, increment PS_CCCCSS_VERSION
Template changes don't appearSmarty compile cacherm -rf var/cache/prod/smarty/compile/*
Infinite loop / timeout{extends} without parent: prefixUse {extends file='parent:catalog/product.tpl'}
HTML tags showing as textSmarty auto-escapingUse {$var nofilter} for HTML variables
Module feature missing after updateStale theme module overridediff override vs module template, update or delete override
Blank page after theme switchMissing theme.yml or wrong parentCheck config, verify parent theme exists
Fonts not loadingWrong relative path in @font-facePath must be relative to the CSS file location
Mobile layout brokenMissing viewport meta or wrong Bootstrap gridCheck _partials/head.tpl for viewport, verify responsive classes

Pliki w tym katalogu, ktore powinienes znac

If you're developing modules or customizing themes, these are the files worth reading (in the parent theme):

  • templates/_partials/head.tpl -- where CSS and meta tags are output. Understand this to debug missing stylesheets.
  • templates/layouts/layout-full-width.tpl -- the page wrapper. Shows you the HTML skeleton: header, content area, footer, hook positions.
  • templates/catalog/product.tpl -- the most complex template. Shows all product page blocks, hook positions, and data variables available.
  • config/theme.yml -- the source of truth for assets, layouts, and theme configuration.
  • assets/css/theme.css -- read (don't edit) to understand the parent's class names and specificity patterns.

For a hands-on guide to creating child themes and customizing templates, see our Child Themes guide in the Knowledge Base.

Next in the Architecture Series: /modules/ Deep Dive -- the complete module lifecycle, from install() to hookDisplay*(), including upgrade scripts, autoloading, and the patterns that 140+ production modules rely on.

Udostępnij ten wpis:
David Miller

David Miller

Ponad dekada praktycznego doświadczenia z PrestaShop. David tworzy wydajne moduły e-commerce skupione na SEO, optymalizacji zamówień i zarządzaniu sklepem. Pasjonat czystego kodu i mierzalnych...

Spodobał Ci się ten artykuł?

Otrzymuj nasze najnowsze porady, przewodniki i aktualizacje modułów prosto na swoją skrzynkę.

Komentarze

Brak komentarzy. Bądź pierwszy!

Zostaw komentarz

Ładowanie...
Do góry