ResourceLoader/Developing with ResourceLoader

Jump to: navigation, search
shortcut: RL/DEV

This is a quick walkthrough about the key points you need to understand to develop in MediaWiki core or an extension with ResourceLoader.


See also Manual:$wgResourceModules

In order for all the ResourceLoader features to function properly it is required to register modules under a symbolic name. We do that by adding the module definition to the configuration variable $wgResourceModules in your extension.json file, or to add the array to ./resources/Resources.php (if you're working in core).

Example for an extension that is compatible with MediaWiki 1.25 and later using extension.json:

"ResourceModules": {
    "ext.myExtension": {
        "scripts": [
        "styles": "css/ext.myExtension.css",
        "messages": [
        "dependencies": [
Because (in this example) ext.myExtension is a module provided by and related to an extension, the module name begins with "ext.".
This defined module consists primarily of two scripts and a CSS file that goes with it. The module also declares that it wants to be able to use two translatable message-keys. Any messages declared here will be available through mw.msg() after your module has loaded. Tip: Pass single resources as a string. Pass multiple resources as an array of strings.
The scripts depend on the module jquery.ui.datepicker. Loading and execution of scripts is fully asynchronous, so it is really important to declare your dependencies, to make sure that they are loaded and available before your scripts try to use/reference them. Notice that we only list the dependency we directly need. Other modules such as jquery and jquery.ui will be automatically loaded to support jquery.ui.datepicker.

There are a multitude of additional properties that a module can have, and these options are mostly intended to influence the delivery behavior.

  • Look at skinStyles and skinScripts if (part of) your code is skin specific
  • Use skipFunction if you are adding polyfills
  • When you need to include translations, you might want to check out languageScripts
  • When you want your module to be loaded in mobile variants as well, use targets. (See also Writing a MobileFrontend friendly ResourceLoader module)

Module structure[edit]

Work in progress
Top module

Two modules for most extensions. A module to be loaded from the <head> containing styles and/or scripts that need to run as soon as possible. This module should generally be a small as possible and be used to:

  • Style output by PHP
  • Insert or modify elements that change the location of anything above the "the fold" (such as inserting a banner that pushes the page down, or a sidebar widgets that pushes another sidebar portlet down, or a content action tab that causes a change in the position of other tabs)
Bottom module (default)
  • Insert or modify elements that are not visible right away (e.g. binding autocompletion to a form element or inserting a small absolutely positioned widget)
  • Anything else
Style modules vs Regular modules

Loading modules[edit]


see also Manual:OutputPage.php and ParserOutput

While building the page, add one or more modules to the page by calling the addModules method on the OutputPage or ParserOutput object and passing it one or more module names (such as "", "" or "ext.myExtension.quux")

$outputPage->addModules( 'example.fooBar' );

OutputPage adds the given module names to the load queue of the page. The client side loader requests all of the components for this module (scripts, styles, messages, dependencies, etc.) and execute them correctly. If your module contains a styles that affect elements outputted by PHP as well as elements created dynamically, then you should split the module. One for styling/enhancing the output, and one for dynamic stuff. The first module should have the "position" property set to "top" (in the module definition in $wgResourceModules), so that ResourceLoader will load it before parsing the rest of the HTML, preventing a FOUC (Flash of unstyled content). The other module doesn't need a "position" property and will simply be loaded asynchronously by the client, not blocking further parsing of the page.

Both should be added to the load queue in the same way, without repeating or forcing any particular loading style. This information is already in the module registry and the client will handle it accordingly.

$outputPage->addModules( array( '', '' ) );

To get a reference to OutputPage object, you can use the BeforePageDisplay hook.


If you have CSS that should be loaded even when JavaScript is unavailable, you can add it to the page with OutputPage::addModuleStyles which will load it with a <link> tag.

This is in contrast with the preferred method OutputPage::addModules which will load modules as a complete package in a single request (scripts, styles, messages) dynamically (from a lightweight client in JavaScript). This is the preferred method because it is more efficient (single request for all resources), it supports dependency resolution and batching only what is not yet loaded (through access to the startup manifest and state of other modules), and is highly cacheable (through access to the startup manifest with all version numbers it can dynamically generate permanently cacheable urls).

Since dependency changes can be deployed independently from caching the page, static loading with addModuleStyles cannot use dependencies. And since you can't dynamically access the latest version of the startup manifest from static HTML without JavaScript execution, it cannot have versions in the urls either and are therefore cached much shorter.

In practice you should only use OutputPage::addModuleStyles for stylesheets that are required for basic presentation of server-generated content (PHP output and the core Skin). Separate this CSS from modules with JavaScript and styling of dynamically-generated content, and don't have JavaScript modules depend on it, otherwise ResourceLoader may load modules twice.


JavaScript files are, like CSS files, also evaluated in the order they are defined in the scripts array.

In the following example, one would use the entry 'scripts' => array( 'foo.js', 'init.js' ) when registering the module.

// foo.js
var Foo = {
    sayHello: function ( $element ) {
        $element.append( '<p>Hello Module!</p>' );
window.Foo = Foo;

// init.js
$( function () {
    // This code must not be executed before the document is loaded. 
    Foo.sayHello( $( '#hello' ) );

The page loading this module would somewhere use $this->getOutput()->addHTML( '<div id="hello"></div>' ); to output the element.

Passing information from PHP to Javascript[edit]

You will often find yourself having to pass information from the server side to the client-side run Javascript. Usually you will do this by using either HTML or the API. In a case where this is not possible, you can pass the information as a Javascript variable. For this, you make a call to addJsConfigVars() on the OutputPage or ParserOutput object. In rare cases where you need to add global configuration variables, you can use the ResourceLoaderGetConfigVars hook. Using ResourceLoaderGetConfigVars can add a lot of extra bytes to every single page, so try to avoid using it.

Client-side (dynamically)[edit]

Gadgets should list their dependencies and compatibility with RL in the options of their definition.

Userscripts, unlike server side modules or gadgets, currently cannot declare dependencies. In this case you must wrap the execution of your code that uses modules in a mw.loader.using block.


mw.loader.using( ['mediawiki.util','mediawiki.Title'] ).then( function () {
    /* This callback is invoked as soon as the modules are available. */
} );

This will guarantee that the module is loaded. Don't worry about multiple (separate) scripts both asking the loader for the same module. ResourceLoader internally ensures nothing will be loaded multiple times. However requests when a request has been made already will tag along the existing request. And if it has already finished, then the loader will immediately run the callback without delay.

If you just want to load the module, and don't need the callback, you can use mw.loader.load( '' ); instead.

Conditional lazy loading[edit]

If you have a script that only needs another module in a certain scenario of the user interface, you could instead create a small init module (ideally loaded server side), and from there use JavaScript to dynamically kick-off a load of the rest of the module.


var $sortableTables = $content.find( 'table.sortable' );
if ( $sortableTables.length ) {
    mw.loader.using( 'mediawiki.util' ).then( function () {
    } );

Parallel execution[edit]

If you have multiple asynchronous tasks, like a an AJAX request and the loading of a ResourceLoader module, then you do not want them to be executed sequentially, simply because you are nesting your dependencies. The best way to do this is using jQuery.when:

// Good: These three processes run in parallel
  $.getJSON( '//' ),
  mw.loader.using( ['mediawiki.util', 'mediawiki.Title', 'jquery.cookie'] ),
).then( function ( fooData ) {
 // This runs when the ajax request is complete, the modules are loaded,
 // and the document is ready
 $( '#example' ).attr( 'href', mw.util.getUrl( ) );
} );

// Bad: These are nested, one waiting before the other starts:
$( function () {
  mw.loader.using( ['mediawiki.util', 'mediawiki.Title', 'jquery.cookie'] ).then( function () {
    $.getJSON( '//' ).then( function ( fooData ) {
      $( '#example' ).attr( 'href', mw.util.getUrl( ) );
    } );
  } );
} );

// Slightly better: preloading with load()
$( function () {
  mw.loader.load( ['mediawiki.util', 'mediawiki.Title', 'jquery.cookie'] );
  $.getJSON( '//' ).then( function ( fooData ) {
      mw.loader.using( ['mediawiki.util', 'mediawiki.Title', 'jquery.cookie'], function () {
        $( '#example' ).attr( 'href', mw.util.getUrl( ) );
      } );
  } );
} );


Your styling resources can be either CSS or, since MediaWiki 1.22, Less files. When writing styles we advice you to follow our conventions.

Media queries[edit]

You can use media queries when you define your modules, to specify when a CSS/Less file applies:

	'styles' => array(
		'print.css'  => array( 'media' => 'print' ),
		'high-resolution.css'  => array( 'media' => 'screen and ( min-width: 982px )' ),

In the above example, the always.css stylesheet will always apply, the print.css stylesheet applies on print (and in the "Printable version" mode), and the high-resolution.css stylesheet applies when the viewport width is at least 982 pixels. The contents of the corresponding CSS/Less file will then be wrapped in the defined media query:

/* Output of print.css by ResourceLoader */
@media print {
	/* Contents of print.css */

/* Output of high-resolution.css by ResourceLoader */
@media screen and ( min-width: 982px ) {
	/* Contents of high-resolution.css */


Our CSS ResourceLoader preprocessor recognizes several annotations that you can use to further optimize CSS.


See also ResourceLoader/Features#Embedding

If you specify a small image (up to 24kB) with the CSS url() function and precede it with the annotation @embed in a CSS comment, then ResourceLoader will embed the image in the CSS stream as a data URI. For example, in MediaWiki 1.25 the '' module includes, which specifies an animated GIF for class feedback-spinner:

.feedback-spinner {
    display: inline-block;
    zoom: 1;
    *display: inline; /* IE7 and below */
    /* @embed */
    background: url(;

If you view a ResourceLoader request for this module, you can see that (unless in debug mode) the ResourceLoader transforms this external image URL into an embedded data URL, with the external https URL as a fallback. Reformatted for clarity, the ResourceLoader response includes:


and browsers that support data URIs in CSS use the embedded image instead of making another network request.

MediaWiki version: 1.22

In MediaWiki 1.22 and newer instead of the @embed annotation, you can use a LESS mixin to specify an image, and the mixin function outputs the @embed directive for you. For example you can use the the .background-image() mixin (in mixins.less in core) in your LESS file:

.mw-foo-bar {
    padding: 4px  0 3px 40px;

See also ResourceLoaderImage, which generates raster images and multiple colored icons from a single source SVG file.


See also ResourceLoader/Features#Flipping

To disable the flipping functionality for one CSS declaration or on an entire ruleset, use the @noflip annotation:

For example:

/* @noflip */ 
.mw-portlet-item { 
    float: left; 
    line-height: 1.25; 

/* This one flips! */ 
.mw-portlet-item { 
    margin-left: 0.75em; 
    /* ... except this one: */ 
    /* @noflip */ 
    margin-top: 0.5em; 
    font-size: 0.75em; 
    white-space: nowrap; 


Toggle debug mode[edit]

ResourceLoader supports complex client-side web applications in production and development environments. As these different environments have different needs, ResourceLoader offers two distinct modes: production mode and debug mode (also known as "development") mode.

Debug mode is designed to make development as easy as possible, prioritizing the ease of identifying and resolving problems in the software over performance. Production mode makes the opposite prioritization, emphasizing performance over ease of development.

It is important to test your code in both debug and production modes. In day-to-day development, most developers will find it beneficial to use debug mode most of the time, only validating their code's functionality in production mode before committing changes.

You can enable debug mode for all page views, or for a single page request (by appending ?debug=true to the URL); see ResourceLoader/Features#Debug mode for details on toggling it.

Server-side exceptions[edit]

ResourceLoader catches any errors thrown during module packaging (such as an error in a module definition or a missing file) in load.php requests. It outputs this error information in a JavaScript comment at the top of its response to that request, for example:

 * exception 'MWException' with message 'ResourceLoaderFileModule::readStyleFile: style file not found: 
 * Problematic modules: {"skin.blueprint.styles":"error"}

You can inspect the request in the network panel in the developer tools for most browsers, or you can copy the load.php URL and load it in a new browser window. Note that the HTTP request with a failing module still returns status 200 OK, it does not return an error.

You can also output errors to a server-side log file by setting up a log file ($wgDebugLogGroups) in $wgDebugLogGroups['resourceloader']. They aren't added to the main debug log ($wgDebugLogFile) since logging is disabled by default for requests to load.php (bug 47960).

Client-side errors[edit]


JavaScript returned by ResourceLoader is executed in the browser, and can have run-time errors. Most browsers do not display these to the user, so you should leave your browser's JavaScript console open during development to notice them.

You can use ResourceLoader's mw.loader.getState() function to check the state of a module, for example enter mw.loader.getState( 'skins.vector.js' ) in your browser's JavaScript console.If it returns:

ResourceLoader knows nothing about the module. Check for typos, check your module definitions carefully.
ResourceLoader knows about the module, but hasn't loaded it on the current page yet. Check your logic for adding the module, either server-side or client-side. You can force it to load by entering mw.loader.load( '' )
Something went wrong, either server-side or during client-side execution, with this module or one of its dependencies. You can look for errors, reload the page in debug mode, or issue a load.php request for the one module.
The module loaded on the current page with no errors.

Breaking cache[edit]

When making frequent changes to code and checking them in a browser, the caching mechanisms designed to improve the performance of web-browsing can quickly become inconvenient. When developing on a system which is not making use of a reverse proxy such as Squid or Varnish, you only need to force your browser to bypass its cache while refreshing. This can be achieved by pressing CTRL+F5 in Internet Explorer, or holding the shift key while clicking the browser's refresh button in most other browsers.

If you are developing behind a reverse proxy, you can either change the values of $wgResourceLoaderMaxage or use ?debug=true to bypass cache since bug 25573 is fixed.

See also[edit]

Documentation FeaturesResourceLoader/Features · VocabularyResourceLoader/Vocabulary · Migration guide (users)ResourceLoader/Migration guide (users) · Migration guide (developers)ResourceLoader/Migration guide for extension developers · Developing with ResourceLoaderResourceLoader/Developing with ResourceLoader · Core modulesResourceLoader/Modules · Mobile supportResourceLoader/Writing a MobileFrontend friendly ResourceLoader module
Project information Status updatesResourceLoader/status · Version 1 Design SpecificationResourceLoader/Version 1 Design Specification · Version 2 Design SpecificationResourceLoader/Version 2 Design Specification · RequirementsResourceLoader/Requirements
Other JavaScript DeprecationsResourceLoader/Legacy JavaScript