DEV Community

Magevanta
Magevanta

Posted on • Originally published at magevanta.com

Magento 2 JavaScript Bundling & RequireJS Optimization

JavaScript is one of the biggest culprits behind slow Magento 2 storefronts. A default installation can easily generate 100+ individual JS file requests per page. Each one adds latency, blocks rendering, and hammers your Time to First Byte (TTFB). In this guide, we'll walk through Magento's built-in bundling system, how RequireJS works under the hood, and the concrete steps you can take to dramatically reduce JS overhead.

Why Magento 2 JS is Slow by Default

Magento 2 uses RequireJS (AMD module format) to load JavaScript dependencies on demand. This is a flexible system, but it comes with a significant downside in production: the browser has to make a waterfall of individual HTTP requests to resolve the dependency graph at runtime.

On a typical category page you'll see:

  • requirejs/require.js
  • requirejs-config.js
  • Dozens of individual *.js modules loaded on demand

Even with HTTP/2 multiplexing, this many round trips add up — especially on mobile or high-latency connections.

Step 1: Enable Production Mode

Before any JS optimisation makes sense, make sure you're running in production mode. Development mode intentionally skips most optimizations.

bin/magento deploy:mode:set production
Enter fullscreen mode Exit fullscreen mode

In production mode Magento automatically:

  • Merges and minifies CSS
  • Deploys static assets to pub/static/
  • Enables RequireJS optimizer output

Run the full static content deploy after switching:

bin/magento setup:static-content:deploy -f en_US nl_NL
Enter fullscreen mode Exit fullscreen mode

Step 2: Enable JavaScript Merging and Minification

In the Admin under Stores → Configuration → Advanced → Developer → JavaScript Settings, enable:

Setting Value
Merge JavaScript Files Yes
Enable JavaScript Bundling Yes
Minify JavaScript Files Yes

Or via CLI:

bin/magento config:set dev/js/merge_files 1
bin/magento config:set dev/js/enable_js_bundling 1
bin/magento config:set dev/js/minify_files 1
bin/magento cache:flush
Enter fullscreen mode Exit fullscreen mode

⚠️ Note: Magento's built-in bundling creates a single large bundle per page type (homepage, category, product, etc.). This reduces request count dramatically but can increase initial payload size. Measure the impact with Lighthouse before and after.

Step 3: Understand the RequireJS Build System

Magento ships with an r.js optimizer workflow. When you run setup:static-content:deploy, Magento processes your requirejs-config.js files and can produce optimised bundles.

The key file is requirejs-config.js — each module can contribute to it via view/frontend/requirejs-config.js. Badly written third-party configs are often the source of JS bloat.

Check for conflicts or redundant map entries:

# Inspect the merged requirejs config for a theme
cat pub/static/frontend/Magento/luma/en_US/requirejs-config.js | python3 -m json.tool | grep -i "map\|paths\|shim" | wc -l
Enter fullscreen mode Exit fullscreen mode

A healthy store should have a focused, clean config. If you're seeing hundreds of entries, a third-party module is likely over-declaring dependencies.

Step 4: Identify Your Heaviest JavaScript Files

Before blindly bundling, know what you're dealing with:

# Find the largest JS files in your static deploy
find pub/static/frontend -name "*.js" -not -name "*.min.js" \
  | xargs wc -c \
  | sort -rn \
  | head -20
Enter fullscreen mode Exit fullscreen mode

Common heavy hitters:

  • jquery.js / jquery-ui.js — often loaded even when not needed on a page
  • mage/validation.js — heavy, only needed on checkout/forms
  • Magento_Catalog/js/product/view/ bundle — large on PDPs

Step 5: Use Advanced JS Bundling (Grunt-based)

Magento's default bundling is coarse-grained. For finer control, use the official Advanced JS Bundling approach with Grunt.

Install dependencies in the Magento root:

npm install
npm install -g grunt-cli
Enter fullscreen mode Exit fullscreen mode

Create a bundle config targeting page types:

// Example: split bundles by page type
bundles: {
  'bundles/default': {
    include: [
      'jquery',
      'jquery/ui',
      'mage/apply/main',
      'mage/apply/scripts',
      'mage/common',
      'mage/dataPost',
      'mage/bootstrap'
    ],
    exclude: [],
    create: true
  },
  'bundles/catalog': {
    include: [
      'Magento_Catalog/js/view/compare',
      'Magento_Catalog/js/price-box',
      'Magento_Catalog/js/product/view/product.types.config'
    ],
    exclude: ['bundles/default'],
    create: true
  }
}
Enter fullscreen mode Exit fullscreen mode

Run the bundling:

grunt exec:mage-build
Enter fullscreen mode Exit fullscreen mode

This produces smaller, page-type-specific bundles so visitors only download what they need per page type.

Step 6: Defer Non-Critical JavaScript

Not all JS needs to block rendering. Use Magento's layout XML to defer scripts that don't need to run at parse time.

In your theme's default.xml:

<block class="Magento\Framework\View\Element\Html\Head\Script" name="my.deferred.script">
    <arguments>
        <argument name="file" xsi:type="string">js/my-script.js</argument>
        <argument name="attributes" xsi:type="string">defer</argument>
    </arguments>
</block>
Enter fullscreen mode Exit fullscreen mode

For third-party blocks loaded in the footer, adding defer or async can shave hundreds of milliseconds off your Largest Contentful Paint (LCP).

Step 7: Lazy-Load Heavy Widgets

RequireJS's require() call is already lazy by nature, but make sure you're not eagerly loading heavy modules on every page. Audit your requirejs-config.js for deps entries — these are loaded eagerly on every page load regardless of whether the functionality is used.

// BAD — loads on every page
var config = {
    deps: ['Magento_SomeModule/js/heavy-widget']
};

// GOOD — only load when the DOM element exists
var config = {
    map: {
        '*': {
            'heavyWidget': 'Magento_SomeModule/js/heavy-widget'
        }
    }
};
Enter fullscreen mode Exit fullscreen mode

Then trigger the load conditionally in your template:

require(['heavyWidget'], function(widget) {
    // Only runs when explicitly required
});
Enter fullscreen mode Exit fullscreen mode

Step 8: Remove Unused Modules' JS

If you've disabled frontend modules (checkout steps, wishlists, product comparison), make sure their JavaScript isn't still being loaded. Use layout XML to remove blocks:

<referenceBlock name="some.js.block" remove="true"/>
Enter fullscreen mode Exit fullscreen mode

Or disable the output of entire modules:

bin/magento module:disable Magento_Wishlist
Enter fullscreen mode Exit fullscreen mode

Every module you disable removes its RequireJS config contributions and associated JS files from the dependency graph.

Measuring the Results

Use these tools to validate your work:

# Count JS requests on a page
curl -s https://yourstore.com/ | grep -o 'src="[^"]*\.js"' | wc -l

# Or use Lighthouse CI
npm install -g @lhci/cli
lhci autorun --collect.url=https://yourstore.com
Enter fullscreen mode Exit fullscreen mode

Target metrics after optimization:

  • JS request count: < 10 (down from 80–120)
  • Total JS transfer size: < 300KB gzipped for above-the-fold
  • Time to Interactive (TTI): < 3.5s on 4G

Common Pitfalls

Bundling breaks checkout: Magento's checkout is notoriously sensitive. Always test the full checkout flow after enabling bundling. If you see JavaScript errors, a module is likely missing from the bundle config.

Cache must be flushed after every config change:

bin/magento cache:flush config full_page block_html
Enter fullscreen mode Exit fullscreen mode

Static deploy must be re-run after any requirejs-config.js change — the merged config is written to pub/static/ at deploy time, not runtime.

Summary

Optimization Impact Effort
Production mode High Low
Merge + minify High Low
Built-in bundling High Low
Advanced Grunt bundling Very High Medium
Defer non-critical scripts Medium Low
Remove unused module JS Medium Medium
Lazy-load heavy widgets Medium Medium

JavaScript performance in Magento 2 isn't about one silver bullet — it's about layering these optimizations. Start with production mode and built-in bundling for immediate wins, then invest in advanced Grunt-based page-type bundling for the biggest long-term payoff.

Every millisecond you shave off JS execution translates directly to better conversion rates. Google's own data shows a 0.1s improvement in load time correlates with an 8% improvement in conversion. That's not a rounding error — that's real revenue.

Top comments (0)