Manual:Coding conventions/JavaScript

Other languages:
English • ‎français • ‎lietuvių • ‎português do Brasil • ‎中文 • ‎日本語
shortcut: CC/JS

This page describes coding conventions for JavaScript in the MediaWiki codebase. See also the general conventions.


We use ESLint as our code quality tool, with a custom config for Wikimedia (eslint-config-wikimedia). The settings for MediaWiki core can be found in .eslintrc.json. Many text editors or IDEs have plugins to provide live linting while you type (see "Integrations" on

To exclude some files or directories from the ESLint analysys (such third-party libraries), you can configure ignore patterns in ESLint. Note that node_modules is excluded by default, and does not need to be ignored. The default convention for Wikimedia projects is to use an .eslintignore file. ESLint also supports configuring ignore patterns via "ignorePatterns" in .eslintrc.json, or "eslintIgnore" in package.json.

The following shows an example ESLint config file using the wikimedia config:

	"root": true,
	"extends": [
	"globals": {
		// Project-wide globals.
	"rules": {
		// Project-wide rule variations. Keep to a minimum.

See .eslintrc.json in MediaWiki core and VisualEditor for examples. MediaWiki extensions must always have their own .eslintrc.json file. There are several different profiles that projects with uncommon needs can set, such as different language flavours, or expected globals. For example, wikimedia/client requires code is ES5-compatible, whereas other profiles have different requirements. wikimedia/server tells ESlint that your code will run on a node server in ES6. wikimedia/qunit tells it that your code will run on a node server in ES6). You should expect to use multiple .eslintrc.json files in a repo to set different environmental expectations for different scopes. This makes sure that you don't accidentally use window methods in code you expect to run on the server, or reference QUnit in production code. .eslintrc.json files can inherit their rules from each other using relative file paths – "extends": "../.eslintrc.json" will use the parent directory's configuration and let you over-ride.

You can install a CLI version of ESlint on your machine with:

$ npm install -g eslint

This will install ESLint globally, which is generally a bad idea. Then in any directory belonging to a project with an .eslintrc.json file, and local npm dependencies installed, run it like this:

# Entire directory, recursive
$ eslint .
$ eslint resources/
# Single file
$ eslint resources/ext.AnExtension.js


We use whitespace conventions:

  • Indentation with tabs.
  • No trailing whitespace.
  • Use blank lines to separate one block of logically related code from another.
  • One space on both sides of binary operators and assignment operators.
  • Keywords followed by a "(" (left parenthesis) must be separated by one space. This gives visual distinction between keywords and function invocations.
  • There should be no space between the function name and left parenthesis of an argument list.
  • There should be one space on the insides of parentheses (such as in if statements, function calls, and arguments lists).
  • Don't use operators as if they are functions (such as delete, void, typeof, new, return, ..).

These and other aspects of our style guide are enforced with ESLint.

Whitespace examples
Correct Wrong = bar + baz;

if ( foo ) { = doBar();

function foo() {
	return bar;

foo = function () {
	return 'bar';

foo = typeof bar;

function baz( foo, bar ) {
	return 'gaz';

baz( 'banana', 'pear' );

foo = bar[ 0 ];
foo = bar[ baz ];
foo = [ bar, baz ];;

if( foo ){ = doBar () ;

function foo () {
	return bar;

foo = function() {

foo = typeof( bar );

function baz(foo, bar) {
	return 'gaz';

baz('banana', 'pear');

foo = bar[0];
foo = bar[baz];
foo = [bar,baz];


( function () {
	// This is the file-level closure.
	// This code executes immediately when the module loads.
	var foo, bar,
		baz = 42,
		quux = 'apple';

	function local( x ) {
		return x * quux.length;

	// Statements
	foo = local( baz );
	bar = baz + foo;

	return baz;
}() );


Avoid leakage of variables from or to other modules by wrapping files in a closure. This gives the contained code its own scope.

This pattern is known as an immediately-invoked function expression (or "iffy"[1]).


Variables must be declared before use, and should be the first statement of the function body. Each assignment must be on its own line. Declarations that don't assign a value should be listed before assignments, on the first line (or longer if there are many).

var foo, bar, baz, bang, boom,
	xyzzy, flob,
	waldo = 42,
	quux = 'apple';

When you have multiple lines for value assignments and non-assigned variables, the extra lines should be indented another level.

var $content,
	$wrapper = $( '<div>' ),
	$button = $( '<a>' )
		.attr( 'href', '#' )
		.text( 'Test' );

Functions should be declared before use. In the function body, function declarations should go after the var statement.

Line lengthEdit

Lines should wrap at no more than 80–100 characters. If a statement does not fit on a single line, split the statement over multiple lines. The continuation of a statement should be indented one extra level.

Function calls and objects should either be on a single line or split over multiple lines with one line for each segment. Avoid closing a function call or object at a different indentation than its opening.

Line breaking examples
Yes No
// One line
if ( && === 'that' ) {
	return { first: 'Who', second: 'What' };
} else { 'first', 'second' );

// Multi-line (one component per line)
if ( && === 'that' &&
	! 'this' )
) {
	// Continuation of condition indented one level.
	// Condition closed at original indentation.
	return {
		first: 'Who',
		second: 'What',
		third: 'I don\'t know'
} else {
		[ 'first', 'array', 'parameter' ],
// Mixed one line and multi-line
if ( && === 'that' &&
	! 'this' ) ) {

	// No varying number of segments per line.
	// Closing parenthesis should be on the left.
	return { first: 'Who', second: 'What',
		third: 'I don\'t know' };

} else { 'first', 'second',
		'third' );

        // Avoid statements looking like they are broken up but still together (looks like a call with one parameter).
		'first', 'second', 'third'
		[ 'first', 'array', 'parameter' ], 'second'


Comments should be on their own line and go over the code they describe.

Within a comment, the opening syntax (e.g. slash-slash, or slash-star) should be separated from the text by a single space, and the text should start with a capital letter. If the comment is a valid sentence, then a full stop should be placed at the end of it.

Use line comments (// foo) within functions and other code blocks (including for multi-line comments).

Use block comments (/* foo */) only for documentation blocks. This helps maintain consistent formatting for inline comments (e.g. not some as blocks and some as multi-line comments, or having to convert from one to the other). It also avoids confusing documentation engines. It also makes it easy to disable parts of the code during development by simply moving the end-comment notation a few lines down, without being short-circuited by an inline block comment.

Be liberal with comments and don't fear file size because of it. All code is automatically minified by ResourceLoader before being shipped.


  • Use strict equality operators (=== and !==) instead of (loose) equality (== and !=). The latter does type coercion.
  • No Yoda conditionals.

Type checksEdit

  • string: typeof val === 'string'
  • number: typeof val === 'number'
  • boolean: typeof val === 'boolean'
  • Function: typeof val === 'function'
  • null: val === null
  • object: val === Object( val )
  • Plain Object: jQuery.isPlainObject( val )
  • Array: Array.isArray( val )
  • HTMLElement: obj.nodeType === Node.ELEMENT_NODE
  • undefined:
    • Local variables: variable === undefined
    • Properties: obj.prop === undefined
    • Global variables: typeof variable === 'undefined'


Use single quotes instead of double quotes for string literals. Remember there are no "magic quotes" in JavaScript i.e. \n and \t work everywhere.

To extract part of a string, use the slice() method for consistency. Avoid the substr(), or substring() methods which are redundant, easily mistaken, and may have unexpectedside-effects.[2][3]


In addition to the browser's native APIs, the only interfaces safe to use as global variables are mw, $ and OO.

Avoid creating additional global variables. Code functionality exposed via a public identifier, should be added as functions and properties of an object within mw, e.g. mw.Echo.

Configuration variables exposed by MediaWiki must be accessed via mw.config. For backward compatibility with legacy user scripts and gadgets, many wg-prefixed configuration keys are also exposed in the global scope. These are deprecated. (See task T72470).


All variables and functions must use lowerCamelCase for their naming. For functions, verb phrases are preferred (so getFoo() instead of foo()).

The only exception are constructors used with the new operator. Those names must start with an uppercase letter (UpperCamelCase). JavaScript has no dedicated syntax for classes or constructors, they are declared as any other function. As such there is no compile-time or run-time warning for instantiating a regular function or omitting the new operator on a constructor. This naming convention is our only defence.

Names with acronyms in them should treat the acronym as a normal word and only uppercase the first letter. For example getHtmlApiSource as opposed to getHTMLAPISource.


See also JQuery

To avoid confusion with raw elements and other variables, prefix variables storing an instance of jQuery with a dollar sign (e.g. $foo = $( '#bar' )). This matters because the DOM (e.g. foo = document.getElementById( 'bar' )) returns null if no elements were found, therefore (since null casts to boolean false) one would test the plain variable like if ( foo ). jQuery objects on the other hand (like any array or object in JavaScript) cast to boolean true. If you confuse a jQuery object with the return value of a DOM method, a condition could fail badly. In such case one would use if ( $foo.length ) instead.

Creating elementsEdit

To create a plain element, use the simple <tag> syntax in the jQuery constructor:

$hello = $( '<div>' )
	.text( 'Hello' );

When creating elements based on the tag name from a variable (which may contain arbitrary html):

// Fetch 'span' or 'div' etc.
tag = randomTagName();
$who = $( document.createElement( tag ) );

Only use $('<a title="valid html" href="#syntax">like this</a>'); when you need to parse HTML (as opposed to creating a plain element).


Different types of collections sometimes look similar but have different behaviour and should be treated as such. This confusion is mostly caused by the fact that arrays in JavaScript look a lot like arrays in other languages, but are in fact just an extension of Object. We use the following conventions:

Avoid using a for-in loop to iterate over an array (as opposed to a plain object). A for-in will iterate over the keys instead of over the indices:

  • keys are strings
  • order not guaranteed
  • index can have gaps
  • might include non-numerical properties


Keys in localStorage and/or sessionStorage should be accessed via or


Keys should start with mw and use camel case and/or hyphens. Do not use underscores or other separators. Examples of real keys:

  • mwuser-sessionId
  • mwedit-state-templatesUsed
  • mwpreferences-prevTab

Beware that contrary to cookies via mw.cookie, there is no wiki prefix or cookie prefix added by default. If values must vary by wiki, you must manually include wgCookiePrefix as part of the key.


Values must be strings. Beware that attempting to store other value types will silently cast to a string (e.g. false would become "false").

Space is limited. Use short and concise values over object structures where possible. A few example:

  • For boolean state (true/false, expanded/collapsed) use "1" or "0".
  • For values that are always numbers, store them as-is and cast with Number on the way out (avoid parseInt).
  • For values that are always strings, store them as-is.
  • For lists of software-defined values, consider comma-separate or pipe-separated if possible.
  • For lists of values that may be user-generated or more complex structures, use JSON.

Eviction strategyEdit

Remember that Local storage does not have any eviction strategy by default. Therefore the following should be avoided:

  • Avoid using user-generated input as part of a key name.
  • Avoid keys containing identifiers for user-generated entities (e.g. user names, category names, page ids, or other user-provided or system-provided variables).
  • In general avoid approaches that involve creating a potentially large number of keys.

For example, if feature needs to store state about a variable entity (e.g. current page), it might make sense to use a single key for this and to limit the stored information only to the last few iterations (LRU), despite it having to parse and serialise a slightly larger amount of data on each time. It's discouraged to store these in separate keys as it would grow uncontrolled. Even with a tracking key, this would be subject to leaks because HTML5 web storage is shared between multiple open browser windows without locking mechanism (race conditions to update the tracking key).

When removing use of a local storage key, or when changing the key name that is used, be sure to provide an eviction strategy for cleaning up old values. Typically mw.requestIdleCallback is used to look for the key and remove it. See T121646 for a more scalable system in the future.

Personal informationEdit

Avoid storing any personal information in the default storage (localStorage) as it remains when a user logs out or closes their browser. Use sessionStorage instead.


  • Be careful to preserve compatibility with left-to-right and right-to-left languages (i.e. float: right or text-align: left), especially when styling text containers. Putting those declarations in CSS file will allow them to be automatically flipped for RTL-languages by CSSJanus in ResourceLoader.
  • Use attr() and prop() appropriately.
    Read more:
  • Consistently quote attribute selector values: [foo="bar"] instead of [foo=bar] (jqbug 8229).
  • As of jQuery 1.4 the jQuery constructor has a new feature that allows passing an object as second argument, like $( '<div>', { foo: 'bar', click: function () {}, css: { .. } } );. Don't use this. It makes code harder to follow, fails on attributes (such as 'size') that are also methods, and is unstable due to this mixing of jQuery methods with element attributes. A future jQuery method or plugin or called "title" might convert an element into a heading, which means the title attribute can also no longer be set through this method. Be explicit and call .attr(), .prop(), .on() etc. directly.


Note that we are currently moving to use JSDoc instead of JSDuck as the latter is no longer maintained. The information below will be updated when we are further ahead in that work.

Use JSDuck to build documentation (see The documentation comment structure is broadly similar to the doxygen format we use in PHP but details differ to accommodate for JavaScript-specific language constructs (such as object inheritance, emitting events and arbitrary augmentation of an constructor's prototype).

Standard RubyGems install: gem install jsduck
See the installation guide for more information.
Generate documentation
$ jsduck
Set up configuration for new projects
Create a JSDuck configuration file (at jsduck.json). See for example JSDuck config for MediaWiki and JSDuck config for OOjs.

Documentation commentsEdit

  • Text in free-form blocks (e.g. description of methods, parameters, return values etc.) should be sentence case.
  • End sentences in a full stop.
  • Continue sentences belonging to an annotation on the next line, indented with one additional space.
  • Value types should be separated by a pipe character. Use only types that are listed in the Types section or the identifier of a different class as specified in your project (e.g. {mw.Title}).
 * Get the user name.
 * Elaborate in an extra paragraph after the first one-line summary in
 * the imperative mood.
 * @param {string} foo Description of a parameter that spans over on the
 * next line of the comment.
 * @param {number} bar
 * @return {string} User name


We use the following annotations. They should be used in the order as they are described here, for consistency. See the JSDuck documentation for more documentation about how these work.

  • @abstract
  • @private
  • @static
  • @class Name (name is optional, engine will guess name from context)
  • @singleton
  • @extends ClassName
  • @mixins ClassName
  • @constructor
  • @inheritable
  • @member
  • @method name (name is optional, guessed)
  • @property name (name is optional, guessed)
  • @inheritdoc
  • @deprecated
  • @param {Type} name Optional text.
  • @return {Type} Optional text.
  • @chainable
  • @throws {Type}
  • @cfg {Type} [name="default value"] (used when a configuration object is passed to the class constructor, use one for each property and don't include those of parent constructors)


Primitive types and special values:

  • boolean
  • number
  • string
  • undefined
  • null

Built-in classes:

  • Object
  • Array
  • Function
  • Date
  • RegExp
  • Error

Browser classes:

  • HTMLElement

Commonly used classes:

  • jQuery

Reuse ResourceLoader modulesEdit

Don't reinvent the wheel. ResourceLoader makes a lot of useful MediaWiki functionality and jQuery plugins available as tested and documented ResourceLoader modules that your code can (literally) depend on. Scan the list of RL modules before rolling your own code.

Follow ResourceLoader best practices.

Final notesEdit

Use CSS for styling many elementsEdit

Don't apply styling to lots of elements at once; this has poor performance. Instead use a common parent's class (or add one) and apply CSS in a .css or .less file. Thanks to ResourceLoader, this will all be loaded in the same HTTP request, so there's no performance penalty for having a separate CSS file. Do not set CSS into inline "style" attributes, don't insert "style" elements from JS either.


jQuery doesn't support environments that have manipulated the Object.prototype as it's considered harmful.


In order to enforce the conventions and/or guidelines, you should consider setting up Continuous integration to check many of them so reviewers don't have to.