Why Some Modules Load CSS and JS on Every Page

385 views

The Root Cause: How PrestaShop's Hook System Handles Assets

If you have ever inspected your PrestaShop store's source code and wondered why a module that only shows a widget on product pages is loading its CSS and JavaScript on your homepage, your category pages, and even your checkout, you are not alone. This is one of the most common performance problems in the PrestaShop ecosystem, and it stems from how the hook system works combined with lazy development practices.

PrestaShop uses a hook-based architecture to let modules inject content, assets, and logic into different parts of a page. When a module needs to add CSS or JavaScript files to the page, it typically does so through one of two hooks: displayHeader or actionFrontControllerSetMedia. Both of these hooks fire on every front office page without exception. There is no built-in mechanism in the hook system itself to restrict a hook call to specific page types. It is entirely the module developer's responsibility to check which page is being loaded and decide whether to add assets or not.

The problem is that many module developers take shortcuts. Instead of writing conditional logic that checks the current controller, they simply register their assets in every hook call. The reasoning is simple: if the assets are always loaded, the module will always work regardless of where its content appears. This fire-and-forget approach guarantees that the module functions correctly, but it guarantees poor performance as well.

How displayHeader Hook Abuse Creates Page Bloat

The displayHeader hook is the primary mechanism by which modules add content to the HTML head section of every page. When a module implements the hookDisplayHeader method, PrestaShop calls that method on every front office page load. Inside this method, modules typically call $this->context->controller->addCSS() and $this->context->controller->addJS() to register their asset files.

Here is what happens in a typical store with 25 installed modules. Fifteen of those modules have a hookDisplayHeader method. Each one adds between one and four CSS and JavaScript files. That means every single page on your store loads 15 to 60 additional HTTP requests just from modules, regardless of whether those modules display anything on the current page.

Consider a concrete example. A module that adds a size guide popup to product pages needs one CSS file for the popup styling and one JavaScript file for the popup behavior. If the developer registers these assets in hookDisplayHeader without any conditional check, those two files load on the homepage, on every category page, on the cart page, on the checkout page, and on every CMS page. The size guide will never appear on any of those pages, but the browser still downloads, parses, and processes the CSS and JavaScript files.

Multiply this by every module that follows the same pattern, and you begin to understand why some PrestaShop stores load 2 MB or more of module assets on pages where most of those assets are completely unnecessary.

The actionFrontControllerSetMedia Hook

PrestaShop 1.7 introduced the actionFrontControllerSetMedia hook as a more modern alternative to displayHeader for asset registration. This hook was designed specifically for registering CSS and JavaScript files using the new registerStylesheet and registerJavascript methods. These methods offer advantages over the older addCSS and addJS functions, including the ability to set loading priority, specify async or defer attributes, and control the position (head or bottom) of script tags.

However, actionFrontControllerSetMedia suffers from the exact same fundamental problem as displayHeader: it fires on every page. A module that registers assets in hookActionFrontControllerSetMedia without checking the current controller loads those assets everywhere, just like the older approach.

The only difference is the registration method, not the loading scope. Whether a developer uses addCSS in displayHeader or registerStylesheet in actionFrontControllerSetMedia, the result is the same if they do not add conditional logic. The assets load globally.

Proper Conditional Loading: What Good Modules Do

A well-developed module checks which page the customer is viewing before loading any assets. The standard way to do this in PrestaShop is to check the controller name. Every front office page is served by a specific controller: IndexController for the homepage, CategoryController for category pages, ProductController for product pages, CartController for the cart, and so on.

Inside the hookDisplayHeader or hookActionFrontControllerSetMedia method, the developer can access the current controller through $this->context->controller. By checking the class name or the php_self property of the controller, the module can decide whether to load its assets. A product reviews module, for instance, should check if the current page is a product page and only load its CSS and JavaScript in that case.

This conditional approach is not difficult to implement. It adds perhaps five to ten lines of code to the hook method. Yet a surprising number of modules, including popular paid modules on the PrestaShop Addons marketplace and well-known free modules, skip this check entirely. The reason is a combination of developer convenience and the fact that PrestaShop does not enforce or even encourage conditional loading in its module development documentation.

Some developers argue that loading assets globally ensures compatibility with custom themes or unusual page configurations where the module's content might appear unexpectedly. While this argument has a grain of truth, it does not justify the performance cost. A better approach is to load assets conditionally by default and provide a configuration option to enable global loading for stores that need it.

The setMedia Method: Best Practices for Module Developers

For module developers reading this article, here are the best practices for asset loading that respect your users' site performance.

First, always use the actionFrontControllerSetMedia hook instead of displayHeader for asset registration in PrestaShop 1.7 and later. The newer hook provides better control over asset loading behavior and keeps your asset registration separate from HTML output.

Second, always check the current controller before registering assets. Use a whitelist approach: define the list of controllers where your module displays content, and only register assets when the current controller is in that list. This is more maintainable than a blacklist approach because new page types added in future PrestaShop versions will not accidentally load your assets.

Third, use registerStylesheet and registerJavascript instead of addCSS and addJS. The newer methods let you specify an id for each asset, which makes it possible for themes and other modules to override or remove your assets cleanly. They also support priority settings that control the order in which assets load.

Fourth, consider whether your JavaScript truly needs to load in the head or whether it can load at the bottom of the page with the defer attribute. JavaScript in the head blocks rendering, meaning the browser cannot display any content until your script finishes downloading and parsing. Moving scripts to the bottom or adding defer eliminates this render-blocking behavior.

Fifth, minimize your asset file sizes. Minify your CSS and JavaScript files before distributing your module. Remove unused CSS rules. Avoid bundling entire libraries like jQuery UI or Bootstrap when you only use a small portion of their functionality. Every kilobyte counts when your module's assets load on thousands of page views per day.

Measuring the Performance Impact of Global Assets

To understand how much global asset loading costs your store, you need concrete measurements. Open Chrome DevTools on your homepage and go to the Network tab. Reload the page and filter requests by the path /modules/. Count the total number of requests and their combined size. This is your module asset overhead on a page where most modules do not display any content.

Now do the same on a product page, where many modules legitimately need their assets. Compare the numbers. On a well-optimized store, the product page should load significantly more module assets than the homepage because that is where most modules display their content. On a poorly optimized store, the numbers are nearly identical because every module loads everything everywhere.

A concrete benchmark from real-world audits: a store with 35 installed modules was loading 1.8 MB of module CSS and JavaScript on the homepage. After implementing conditional loading on all modules, the homepage module assets dropped to 340 KB. The product page, where most modules legitimately needed their assets, went from 2.1 MB to 1.4 MB. The homepage load time improved by 1.3 seconds and the Lighthouse performance score went from 42 to 71.

These numbers are not unusual. Global asset loading is one of the single largest sources of unnecessary page weight in PrestaShop stores, and fixing it often produces the most dramatic performance improvements.

How to Identify Offending Modules in Your Store

Finding which modules load assets globally requires a systematic approach. Start with the Network tab in DevTools as described above. For each module asset you find loading on the homepage, ask yourself: does this module display any content on the homepage? If the answer is no, that module is loading assets unnecessarily.

Another approach is to use PrestaShop's debug profiling mode. When profiling is enabled (by setting _PS_DEBUG_PROFILING_ to true in your config/defines.inc.php), PrestaShop displays detailed hook execution data at the bottom of every page. This data shows you exactly which modules execute on each hook and how long they take. Any module that executes on displayHeader or actionFrontControllerSetMedia but does not execute on any display hook that produces visible output on the current page is a candidate for optimization.

You can also check programmatically by examining each module's source code. Look at the hookDisplayHeader and hookActionFrontControllerSetMedia methods in the module's main PHP file. If the method contains addCSS, addJS, registerStylesheet, or registerJavascript calls without any conditional checks on the controller name, that module loads assets globally.

For stores with many modules, this manual review can be time-consuming. A practical shortcut is to focus on the largest assets first. Sort the module assets in the Network tab by size, and start investigating the largest files. A 200 KB JavaScript file loading globally is a much bigger problem than a 3 KB CSS file, so prioritize accordingly.

Requesting Fixes from Module Developers

When you identify a module that loads assets globally, your first step should be to contact the developer and request a fix. Most professional module developers are receptive to performance improvement requests, especially when you can provide specific data about the impact.

Write a clear, technical message that explains the problem. Mention that the module's hookDisplayHeader method loads CSS and JavaScript on every page without checking the current controller. Specify which pages actually need the assets and which pages are loading them unnecessarily. Include the file sizes and the estimated performance impact. If you have Lighthouse scores showing the before and after when the module's assets are blocked, include those.

If the developer is responsive, they will typically release an update within a few weeks that adds conditional loading. If the developer is unresponsive or dismisses the concern, you have several options: implement the conditional loading yourself by editing the module code, use a performance module that can selectively block assets on specific pages, or find an alternative module that follows better development practices.

Workarounds When You Cannot Modify the Module

Sometimes you cannot modify a module directly. It might be encrypted with IonCube, it might be a module you do not want to maintain a fork of, or the module might overwrite your changes on every update. In these cases, you need workarounds.

One effective workaround is to create a small custom module that unhooks the offending module from displayHeader and actionFrontControllerSetMedia, then re-adds the assets only on the pages where they are needed. This custom module would use the actionFrontControllerSetMedia hook with a high priority (so it executes after the offending module) and call Media::unregisterStylesheet and Media::unregisterJavascript to remove the globally loaded assets. Then, on pages where the assets are actually needed, it would re-register them.

Another workaround is to use the Theme.yml file to override module assets. In PrestaShop 1.7 and later, the theme's theme.yml configuration file can remove specific module assets from loading. This is a theme-level solution that persists across module updates. However, it removes the assets from all pages, not selectively, so you would need to combine it with re-adding the assets on specific pages through a custom module or theme template.

A third option, available in PrestaShop 8.x, is to use the asset management system's built-in priority and removal features. PrestaShop 8 improved the asset pipeline to give themes more control over which module assets load. Check your theme's documentation for specific instructions on how to leverage these features.

Regardless of which approach you choose, always test thoroughly after making changes. Removing a module's CSS can break its visual appearance, and removing its JavaScript can break interactive features. Test every page type where the module should still function correctly.

Prevention: Evaluating Modules Before Installation

The best way to avoid global asset loading problems is to evaluate modules before you install them. While this is not always possible, there are checks you can perform.

If the module provides a demo store, visit it and inspect the homepage with DevTools. Check whether the module's assets load on pages where the module does not display content. If assets load globally on the demo, they will load globally on your store too.

If you have access to the module's source code before purchasing (some marketplaces provide code previews), look at the hookDisplayHeader and hookActionFrontControllerSetMedia methods. Check for conditional loading logic. The absence of any controller check is a red flag.

Read the module's reviews and support forum. Performance-conscious users often report global asset loading issues in reviews. If multiple users have complained about the module slowing down their store, take that feedback seriously.

Finally, consider the developer's overall code quality. Developers who follow best practices in one area tend to follow them in others. If a module's code is clean, well-documented, and follows PrestaShop coding standards, it is more likely to handle asset loading correctly. If the code is messy and poorly structured, global asset loading is probably just one of many problems.

For more details, read our guides: PrestaShop Hooks Explained: How Modules Talk to Your Store and Why Module Quality Matters More Than Module Quantity.

Was this answer helpful?

Still have questions?

Can't find what you're looking for? Send us your question and we'll get back to you quickly.

Loading...
Back to top