RequireJS and AMD

Magento loads storefront JavaScript as AMD modules, and each module ships one requirejs-config.js that tells RequireJS where they live.

Syntaxdefine(['jquery', 'Vendor_Notice/js/helper'], function ($, helper) { /* ... */ });

RequireJS is the module loader Magento uses for storefront and admin JavaScript. AMD, short for Asynchronous Module Definition, is the file format it reads. A file wraps itself in define(), lists the modules it needs, and receives them as function arguments. Nothing becomes a global variable, and nothing loads until something asks for it.

Module ids are paths

A module id looks like Vendor_Notice/js/stock-notice. RequireJS resolves it to app/code/Vendor/Notice/view/frontend/web/js/stock-notice.js once static content has been published. The id has no .js extension and no leading slash. Short ids such as jquery and underscore are aliases the framework has already set up for files in lib/web.

requirejs-config.js

A module may ship one requirejs-config.js per area: view/frontend, view/adminhtml, or view/base for both. It declares a single var config object. Magento 2.4.9 reads five keys: map (aliases for module ids), paths (aliases for files and URLs), deps (modules loaded on every page), shim (dependencies for libraries that are not AMD) and config (settings handed to modules, including mixins). Every module's file is merged, in module load order, into one generated file at pub/static/frontend/Vendor/theme/en_US/requirejs-config.js.

Making a change take effect

That merged file is a static asset, so editing your copy changes nothing in the browser on its own. In developer mode run bin/magento cache:flush, then delete pub/static/frontend and var/view_preprocessed. In production mode run bin/magento setup:static-content:deploy -f.

Example

Example · javascript
// app/code/Vendor/Notice/view/frontend/requirejs-config.js

var config = {
    map: {
        '*': {
            stockNotice: 'Vendor_Notice/js/stock-notice'
        }
    },
    paths: {
        slick: 'Vendor_Notice/js/lib/slick.min'
    },
    shim: {
        slick: {
            deps: ['jquery']
        }
    },
    deps: [
        'Vendor_Notice/js/boot'
    ]
};

// After editing this file nothing changes in the browser until the merged
// config is rebuilt. Developer mode:
//   bin/magento cache:flush
//   rm -rf pub/static/frontend var/view_preprocessed
// Production mode:
//   bin/magento setup:static-content:deploy -f
//
// On the storefront: 'stockNotice' now works as a component name in a
// template, 'slick' loads after jQuery, and boot.js runs on every page.

When to use it

  • Register a short alias so a template can start your component by name instead of by full module path.
  • Make a non-AMD carousel library usable by declaring its jQuery dependency in shim.
  • Run analytics code on every storefront page without editing a single template.
  • Work out why the console says "Script error for" by checking which area directory the file was placed in.

More examples

The AMD module the config points at

A module that returns a plain function is the simplest shape. Magento calls it with the configuration object and the DOM element it was attached to. Reach for this when the component does one small thing to one element and has no state worth managing.

Example · javascript
// app/code/Vendor/Notice/view/frontend/web/js/stock-notice.js

define([
    'jquery',
    'mage/translate'
], function ($, $t) {
    'use strict';

    return function (config, element) {
        var message = config.qty <= config.threshold
            ? $t('Only a few left')
            : $t('In stock');

        $(element).text(message);
    };
});

Shorthand and longhand for the dependency list

The second form is sugar for the first. It reads better in a long module, but it only works when the id is a literal string, so a computed id still needs the array form or an inline require.

Example · javascript
// app/code/Vendor/Notice/view/frontend/web/js/boot.js

// Longhand: dependencies listed in an array, received as arguments.
define(['jquery', 'underscore'], function ($, _) {
    'use strict';

    return { ready: true };
});

// Shorthand: no array, require() called inside. RequireJS scans the body
// for require() calls and loads those ids first. The two forms are the
// same thing, and you will see both in core code.
define(function (require) {
    'use strict';

    var $ = require('jquery'),
        _ = require('underscore');

    return { ready: true };
});

Loading a module on demand

Use an inline require when a dependency is only needed after an interaction. It keeps the module out of the initial page load, which matters on a category page where most visitors never click.

Example · javascript
// app/code/Vendor/Notice/view/frontend/web/js/open-help.js

define(['jquery'], function ($) {
    'use strict';

    return function (config, element) {
        $(element).on('click', function () {
            // require() inside a handler: the modal code is only
            // downloaded the first time someone clicks.
            require(['Magento_Ui/js/modal/modal'], function (modal) {
                modal({ title: config.title }, $(config.contentSelector));
                $(config.contentSelector).modal('openModal');
            });
        });
    };
});

Discussion

  • Be the first to comment on this lesson.