Guides Guide

PrestaShop Child Themes: Classic & Hummingbird Customization Guide

Build PrestaShop child themes for Classic and Hummingbird: template overrides, SCSS customization, JavaScript hooks, and PS 9 changes.

What Is a Child Theme and Why It Matters

A child theme inherits templates and assets from a parent theme while letting you override only what you change. Without one, every CSS tweak or template edit lives in the parent's files — and gets overwritten on the next update.

A child theme is a transparent overlay. PrestaShop looks through the child first. If it finds a file, it uses it. If not, it falls through to the parent. You only include what you change.

Use a child theme for targeted modifications — colors, layout tweaks, template overrides, custom CSS/JS. Build a full custom theme only when you need a fundamentally different architecture.

Parent ThemePS VersionFrameworkBest For
Classic1.7.x / 8.xBootstrap 4, jQueryMaximum compatibility, stable base
Hummingbird9.1+Bootstrap 5.3, TypeScriptModern stores, PS 9 readiness

For new projects on PrestaShop 9.1+, choose Hummingbird — it is the future-facing default and the only actively maintained theme by the PrestaShop project.

Generate Your Child Theme

Skip the manual setup — configure your child theme and download a ready-to-install ZIP with custom.css, custom.js, and template overrides pre-configured.

Open Child Theme Generator →

Child Theme Structure

A child theme lives alongside its parent inside /themes/. The minimum viable child theme requires just two files:

themes/my-child-theme/
  config/
    theme.yml             <-- required: theme configuration
  preview.png             <-- required: 200x200 theme preview

As customizations grow, the directory mirrors the parent's structure but only contains modified files:

themes/my-child-theme/
  config/theme.yml
  assets/
    css/custom.css
    js/custom.js
  templates/
    catalog/product.tpl
    _partials/header.tpl
  modules/
    ps_featuredproducts/views/templates/hook/
      ps_featuredproducts.tpl
  translations/en-US/Shop/Theme.xlf
  preview.png
Only add files you are changing. Every file you add is a file you must maintain. An empty child theme that inherits everything is better than one cluttered with unchanged copies.

The theme.yml Configuration

The config/theme.yml is the heart of your child theme. The critical line is parent: classic (or parent: hummingbird) — this establishes the inheritance chain:

parent: classic
name: my-child-theme
display_name: My Child Theme
version: 1.0.0
author:
  name: "Your Name"
  email: "you@example.com"
  url: "https://yoursite.com"

meta:
  compatibility:
    from: 1.7.0.0
    to: ~

assets:
  use_parent_assets: true

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

Creating a Child Theme for Classic

Classic is PrestaShop's original default since 1.7. The process is four steps:

# 1. Create the directory
mkdir -p themes/my-classic-child/config

# 2. Create config/theme.yml with parent: classic (see above)

# 3. Add preview.png (200x200)

# 4. Activate via Back Office > Design > Theme & Logo
#    Or CLI: php bin/console prestashop:theme:enable my-classic-child

With use_parent_assets: true and no overrides, you get a pixel-perfect copy of Classic. Templates, CSS, JS, image types, layouts, and module template overrides are all inherited.

Creating a Child Theme for Hummingbird

Hummingbird uses Bootstrap 5.3 (CSS custom properties), CSS Layers (@layer), TypeScript compiled with Webpack 5, and BEM-like naming (.product__name, .product__description-short). Setup is identical to Classic — create theme.yml with parent: hummingbird and compatibility from: 9.0.0. The difference is in asset customization.

Hummingbird's CSS Layer System

Hummingbird defines this layer order in its SCSS:

@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. Place custom rules in the appropriate layer:

@layer ps-components {
  .product__name {
    font-family: 'Your Custom Font', sans-serif;
  }
}

@layer ps-pages {
  .page--category .product-miniature {
    border: 1px solid #eee;
    border-radius: 8px;
  }
}

SCSS Compilation

For custom SCSS, add a build step. Create package.json with sass as a dev dependency and scripts for build:css (sass src/scss/custom.scss assets/css/custom.css --style=compressed) and watch:css. Then npm install && npm run build:css to compile.

Dart Sass can prepend a UTF-8 BOM to compiled CSS, causing browsers to silently ignore rules. Strip it: sed -i '1s/^\xEF\xBB\xBF//' assets/css/custom.css

Template Overrides

Template overrides are where child themes shine. PrestaShop uses Smarty, which supports a clean inheritance model.

Template Resolution Order

  1. Child themethemes/my-child/templates/
  2. Parent themethemes/hummingbird/templates/

Method 1: Full Copy (Simple, Less Maintainable)

Copy the parent template and edit it. Your version replaces the parent's entirely:

cp themes/hummingbird/templates/catalog/product.tpl \
   themes/my-child/templates/catalog/product.tpl

Downside: You own the entire file. Parent updates to that template will not reach you.

Method 2: Smarty {extends} (Preferred)

Extend the parent template and override only specific blocks:

{extends file='parent:catalog/listing/category.tpl'}

{block name='product_list_header'}
  <div class="custom-category-header">
    <h1>{$category.name}</h1>
    <p>{$listing.pagination.total_items} products</p>
  </div>
{/block}

The parent: prefix is critical — without it, Smarty finds the child's own file and creates an infinite loop.

Block Modifiers

Instead of replacing a block entirely, append or prepend content:

{extends file='parent:catalog/product.tpl'}

{block name='product_description' append}
  <div class="shipping-notice">Free shipping on orders over $50</div>
{/block}

{block name='product_prices' prepend}
  <span class="price-badge">Best Price</span>
{/block}

Hummingbird's Block Structure

Key override points in Hummingbird's product page (catalog/product.tpl):

{block name='product_cover_thumbnails'}   — product images
{block name='product_header'}              — product name (h1)
{block name='product_manufacturer'}        — brand link
{block name='product_prices'}              — price display
{block name='product_description_short'}   — short description
{block name='product_variants'}            — combination selectors
{block name='product_add_to_cart'}         — add-to-cart button
{block name='product_tabs'}               — description/details accordion
{block name='product_accessories'}         — related products

To find available blocks for any page, read the parent theme's template file — every {block name='...'} is an override point.

CSS Customization

Register CSS in theme.yml

assets:
  use_parent_assets: true
  css:
    all:
      - id: my-child-custom-style
        path: assets/css/custom.css
        media: all
        priority: 200

Higher priority values load later, letting you override parent styles. For Hummingbird, prefer @layer declarations instead of fighting specificity. For Classic, match the parent's selector depth. Avoid !important.

Changing the Color Scheme

For Hummingbird (Bootstrap 5), override CSS custom properties:

:root {
  --bs-primary: #2563eb;
  --bs-primary-rgb: 37, 99, 235;
  --bs-body-font-family: 'Inter', system-ui, sans-serif;
}

JavaScript Customization

Registering JS in theme.yml

assets:
  use_parent_assets: true
  js:
    all:
      - id: my-child-custom-js
        path: assets/js/custom.js
        priority: 200

Replace all with a page name to scope scripts: product, category, cart, checkout, cms, index (homepage), or any controller name.

PrestaShop's JavaScript Events

Your scripts can listen for PrestaShop events through the prestashop global:

prestashop.on('updatedProduct', function(event) { /* combination changed */ });
prestashop.on('updateCart', function(event) { /* cart updated */ });

jQuery Availability

In Classic, jQuery is loaded globally. In Hummingbird, jQuery 3.x is still loaded for module compatibility, but the theme itself uses TypeScript. For new child theme code, prefer vanilla JS.

Do not remove jQuery from your child theme — you will break third-party modules that depend on it. But for your own code, vanilla JS gives better performance and future compatibility.

Module Template Overrides

You can override how any module renders by placing templates in your theme's modules/ directory.

Resolution Order

  1. Child theme: themes/my-child/modules/module_name/views/templates/...
  2. Parent theme: themes/parent/modules/module_name/views/templates/...
  3. Module itself: modules/module_name/views/templates/...

How to Override

Find the module's template (e.g., modules/ps_featuredproducts/views/templates/hook/ps_featuredproducts.tpl), copy it to the matching path under themes/my-child/modules/, and edit the copy. The module can update freely without affecting your version.

Theme overrides take absolute priority. If a module updates its template to fix a security issue and your theme has an older copy, the old version continues to be used. Periodically compare your overrides against current module versions.

Translations in Child Themes

Translation resolution order:

  1. Database translations (Back Office > International > Translations)
  2. Child theme translations/ directory
  3. Parent theme translations/ directory
  4. Module translations
  5. Core translations

The easiest approach is the Back Office (International > Translations). For version-controlled translations, create XLIFF files at themes/my-child/translations/en-US/Shop/Theme/Global.xlf:

<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
  <file original="shop-theme-global" source-language="en-US"
        target-language="en-US" datatype="plaintext">
    <body>
      <trans-unit id="add_to_cart" approved="yes">
        <source>Add to cart</source>
        <target>Add to bag</target>
      </trans-unit>
    </body>
  </file>
</xliff>

For multilingual stores, create files per locale (translations/fr-FR/, translations/de-DE/). Clear cache after changes: php bin/console cache:clear

Common Customizations

Custom Header Layout

{extends file='parent:_partials/header.tpl'}

{block name='header_nav'}
  <nav class="custom-header-nav container">
    <div class="row align-items-center">
      <div class="col-md-3">{hook h='displayNav1'}</div>
      <div class="col-md-6 text-center">
        <a href="{$urls.base_url}"><img src="{$shop.logo}" alt="{$shop.name}"></a>
      </div>
      <div class="col-md-3 text-end">{hook h='displayNav2'}</div>
    </div>
  </nav>
{/block}

Custom Fonts

Place WOFF2 files in assets/fonts/ and reference them in CSS:

@font-face {
  font-family: 'CustomFont';
  src: url('../fonts/CustomFont-Regular.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
body { font-family: 'CustomFont', system-ui, sans-serif; }
Always use font-display: swap to prevent invisible text during loading. Preload critical fonts: <link rel="preload" as="font" type="font/woff2" crossorigin>.

Rearranging the Product Page

{extends file='parent:catalog/product.tpl'}

{block name='product_description_short'}
  <div class="product__description-short">{$product.description_short nofilter}</div>
  {if $product.description}
    <div class="product__description-full mt-3">{$product.description nofilter}</div>
  {/if}
{/block}

{block name='product_description'}{/block} {* removed from tabs *}

PS 9 Theme Changes

  • Hummingbird is the default. Classic remains available but active development targets Hummingbird.
  • Smarty stays for the front office. Twig is for the admin panel only. Your {extends} and {block} overrides work as before.
  • Bootstrap 5.3 replaces Bootstrap 4. Review the migration guide if moving from Classic.
  • CSS Layers (@layer) change how specificity works in Hummingbird.
  • WebP default for images. New framework key in theme.yml: bootstrap-v5.3.3.
  • Some legacy JS globals removed. Test thoroughly when migrating from PS 8.

Twig may eventually reach the front office in a future major version. Writing clean templates with minimal overrides will ease any future transition.

Debugging Theme Issues

First, clear all caches: php bin/console cache:clear. For development, set Template cache to "Force compilation" and disable CCC in Advanced Parameters > Performance.

During development, always force template compilation and disable CCC. Without this, you are debugging cached files.

Enable Smarty debug by appending ?SMARTY_DEBUG to any front URL. This shows loaded templates, file paths, and variables. Enable it in config/defines.inc.php with _PS_SMARTY_FORCE_COMPILE_ and _PS_SMARTY_CONSOLE_ set to 1.

Common Errors

SymptomLikely CauseFix
Blank pageSmarty syntax errorCheck var/logs/
Template not foundWrong path or missing parent: prefixVerify path mirrors parent exactly
Infinite loop / timeout{extends} without parent: prefixUse {extends file='parent:...'}
Block content missingBlock name mismatchCheck exact names in parent template
CSS not appliedCache or specificity issueClear cache, check priority in DevTools
Double-encoded HTMLAuto-escaping pre-escaped contentUse {$var nofilter}

If cache persists, also delete var/cache/prod/smarty/compile/ and var/cache/prod/smarty/cache/.

Upgrading with Child Themes

This is where child themes pay for themselves. When the parent updates:

  • Files only in the parent — updated normally, child inherits them automatically.
  • Files overridden in the child — child's version continues to be used, parent update is invisible.
  • Custom child-only files — completely unaffected.

Before updating: back up, list your overrides (find themes/my-child/templates -name "*.tpl"), update in a staging environment (local dev guide), diff changed parent templates against your overrides, and test every customized page.

Targeted {extends} overrides are superior to full-file copies. With block overrides, new parent blocks are automatically included. Full-file copies miss them entirely.

Child Theme Pre-Launch Checklist

Configuration: theme.yml has correct parent: and name: (lowercase, no spaces). use_parent_assets: true is set. preview.png exists. Compatibility range matches your PS version.

Templates: All overrides use {extends file='parent:...'} where possible. Block names exactly match the parent. No Smarty errors in var/logs/. Module overrides tested against current module versions. nofilter used for HTML variables.

Assets: Custom CSS loads after parent (high priority). No !important overuse. Fonts use font-display: swap and WOFF2. No JS console errors. CCC enabled without breaking anything.

Testing: Desktop (Chrome, Firefox, Safari, Edge), mobile (iOS Safari, Android Chrome), responsive breakpoints. Verify: homepage, categories, product, cart, checkout, customer account, CMS, search, 404.

Maintenance: Child theme in Git. Overridden templates documented. Build process documented (see our dev tools guide). Team knows not to edit parent files.

Loading...
Back to top