Skip to main content
Extensions add functionality to MediaWiki without modifying core files. They integrate with MediaWiki through a standardized registration system based on extension.json and a set of well-defined extension points including hooks, services, special pages, resource modules, and more.

Loading an Extension

Extensions are loaded in LocalSettings.php using wfLoadExtension():
// In LocalSettings.php
wfLoadExtension( 'FoodProcessor' );
This tells MediaWiki to read extensions/FoodProcessor/extension.json and register everything the extension declares.

extension.json Structure

Every extension must have an extension.json file at its root. The file conforms to the MediaWiki extension schema (v2).

Required Fields

{
    "manifest_version": 2,
    "name": "FoodProcessor"
}
Only manifest_version and name are required. All other fields are optional.

Identification Fields

{
    "manifest_version": 2,
    "name": "FoodProcessor",
    "version": "1.0.0",
    "author": [ "Alice Smith", "Bob Jones" ],
    "url": "https://www.mediawiki.org/wiki/Extension:FoodProcessor",
    "description": "Processes food-related wiki content.",
    "license-name": "GPL-2.0-or-later",
    "type": "parserhook"
}
The type field categorizes the extension. Common values include api, antispam, editor, media, parserhook, semantic, specialpage, and other.

Version Constraints with requires

Use the requires field to declare minimum versions of MediaWiki, PHP, or other extensions:
{
    "requires": {
        "MediaWiki": ">= 1.39.0",
        "platform": {
            "php": ">= 8.0"
        },
        "extensions": {
            "AnotherExtension": ">= 2.0.0"
        }
    }
}
Version constraint strings follow semantic versioning operators (>=, <, ~, etc.). The requires.MediaWiki field is checked at load time and will prevent the extension from loading if the constraint is not satisfied.

AutoloadNamespaces (PSR-4)

Map PHP namespaces to directories for autoloading:
{
    "AutoloadNamespaces": {
        "MediaWiki\\Extension\\FoodProcessor\\": "includes/"
    }
}
Namespace keys must end with a backslash, and directory values must end with a forward slash. This enables PSR-4 autoloading for all classes under the given namespace.
AutoloadNamespaces is for PSR-4 autoloading. The older AutoloadClasses field maps individual class names to file paths and is still supported but not recommended for new extensions.

MessagesDirs for i18n

Point to directories containing JSON internationalization files:
{
    "MessagesDirs": {
        "FoodProcessor": [
            "i18n"
        ]
    }
}
The directory should contain files like en.json, de.json, etc. Each file maps message keys to their localized strings:
{
    "@metadata": { "authors": [ "Alice" ] },
    "foodprocessor-mash-label": "Mash",
    "foodprocessor-mash-desc": "Mash the selected items."
}

Hooks and HookHandlers

Register hook handlers using the new-style HookHandlers + Hooks pair:
{
    "HookHandlers": {
        "main": {
            "class": "MediaWiki\\Extension\\FoodProcessor\\HookHandler",
            "services": [ "RevisionStore" ]
        }
    },
    "Hooks": {
        "ArticleSaveComplete": "main",
        "ParserFirstCallInit": "main"
    }
}
See the Hook System page for full details on handler classes, service injection, and deprecation.

ServiceWiringFiles

Register additional service wiring files:
{
    "ServiceWiringFiles": [
        "includes/ServiceWiring.php"
    ]
}
See the Service Container page for how to write service wiring.

ResourceModules

Register JavaScript and CSS via ResourceLoader:
{
    "ResourceFileModulePaths": {
        "localBasePath": "modules",
        "remoteExtPath": "FoodProcessor/modules"
    },
    "ResourceModules": {
        "ext.foodprocessor.main": {
            "scripts": [ "foodprocessor.js" ],
            "styles": [ "foodprocessor.css" ],
            "messages": [
                "foodprocessor-mash-label"
            ],
            "dependencies": [ "mediawiki.api" ]
        }
    }
}
localBasePath is relative to the extension root. remoteExtPath is relative to $wgExtensionAssetsPath on the client. Module names should use the ext.<extensionname>.<module> convention.

Complete Example

{
    "manifest_version": 2,
    "name": "FoodProcessor",
    "version": "1.0.0",
    "author": "Alice Smith",
    "url": "https://www.mediawiki.org/wiki/Extension:FoodProcessor",
    "description": "Processes food-related wiki content.",
    "license-name": "GPL-2.0-or-later",
    "type": "parserhook",
    "requires": {
        "MediaWiki": ">= 1.39.0",
        "platform": {
            "php": ">= 8.0"
        }
    },
    "AutoloadNamespaces": {
        "MediaWiki\\Extension\\FoodProcessor\\": "includes/"
    },
    "MessagesDirs": {
        "FoodProcessor": [ "i18n" ]
    },
    "HookHandlers": {
        "main": {
            "class": "MediaWiki\\Extension\\FoodProcessor\\HookHandler"
        }
    },
    "Hooks": {
        "ParserFirstCallInit": "main"
    },
    "ServiceWiringFiles": [
        "includes/ServiceWiring.php"
    ],
    "ResourceFileModulePaths": {
        "localBasePath": "modules",
        "remoteExtPath": "FoodProcessor/modules"
    },
    "ResourceModules": {
        "ext.foodprocessor.main": {
            "scripts": [ "foodprocessor.js" ],
            "styles": [ "foodprocessor.css" ]
        }
    }
}

Extension Registration Internals

The includes/Registration/ directory contains the classes that process extension.json:
FileRole
ExtensionRegistry.phpSingleton registry; reads and caches extension data
ExtensionProcessor.phpParses extension.json and merges data into core
ExtensionJsonValidator.phpValidates extension.json against the schema
VersionChecker.phpEvaluates requires version constraints
Processor.phpBase class for extension and skin processors
To check at runtime whether an extension is loaded, use:
use MediaWiki\Registration\ExtensionRegistry;

if ( ExtensionRegistry::getInstance()->isLoaded( 'FoodProcessor' ) ) {
    // extension is present
}

Extension vs. Skin

Skins use the same wfLoadSkin() mechanism and a skin.json file with an identical structure. The type field in extension.json can be "skin", but conventionally skins use skin.json so they appear separately in Special:Version. See the Skins page for details.

Build docs developers (and LLMs) love