Understanding MediaWiki's Skin System: Customizing Your Wiki's Appearance

A technical look at the MediaWiki skin system: anatomy of a skin, minimal working examples, customization routes, and common pitfalls when building your own.

A MediaWiki skin controls how pages are presented: which layout, which stylesheets, which navigation elements exist around the content. The default install ships with Vector (legacy and the 2022 redesign), Timeless, MonoBook and Minerva Neue (the mobile skin used on Wikipedia). Skins matter beyond looks: they define reading flow, navigation density and mobile behavior, so a wiki's skin choice is partly a product decision. This article covers how the skin system works and how to build or customize your own.

Anatomy of a skin

A modern skin consists of three parts, all living in skins/YourSkinName/:

  • The skin class — a PHP class extending SkinTemplate (which itself extends Skin), declaring the skin name and which template to use
  • The template class — a PHP class extending BaseTemplate, whose execute() method renders the page structure: header, content area, sidebar, footer
  • ResourceLoader modules — CSS, LESS and JavaScript assets declared in the skin's skin.json

Skins are registered with skin.json nowadays (manifest version 2), which also declares their ResourceLoader modules without touching LocalSettings.php:

{
    "name": "MySkin",
    "license-name": "GPL-2.0-or-later",
    "type": "skin",
    "ValidSkinNames": {
        "myskin": "SkinMySkin"
    },
    "ResourceModules": {
        "skins.myskin": {
            "class": "ResourceLoaderSkinModule",
            "features": [ "normalize", "elements", "interface", "toc" ],
            "styles": [ "resources/skin.less" ]
        }
    }
}

A minimal skin

The skin class is deliberately thin — the logic lives in the template:

class SkinMySkin extends SkinTemplate {
    public $skinname = 'myskin';
    public $template = 'MySkinTemplate';

    public function initPage( OutputPage $out ) {
        parent::initPage( $out );
        $out->addModuleStyles( [ 'skins.myskin' ] );
        $out->addModules( [ 'skins.myskin.js' ] );
    }
}

The template class receives all page data through the get() and html() helpers. A functional skeleton:

class MySkinTemplate extends BaseTemplate {
    public function execute() {
        $this->html( 'headelement' ); // <head>, title, scripts

        echo '<div class="site-header">'
            . $this->get( 'sitename' )
            . '</div>';

        echo '<div id="content">';
        echo $this->get( 'newtalk' )
            ? '<div class="usermessage">You have new messages</div>'
            : '';
        echo '<h1 id="firstHeading">' . $this->get( 'title' ) . '</h1>';
        echo $this->html( 'bodycontent' );
        echo '</div>';

        $this->html( 'bottomscripts' );
        $this->html( 'footer' );
    }
}

The data available to a template — page title, body, site name, navigation links, footer — is documented on Manual:Skinning, and the SkinTemplateGeneratePageActions/SkinTemplateNavigation hooks are how skins and extensions adjust the menus.

Customizing an existing skin instead

Most wikis never write a skin from scratch. Practical options, in increasing order of effort:

  • Site-wide CSSMediaWiki:Common.css plus the SkinModule variables are enough for recoloring and small layout tweaks
  • Forking — copy an existing skin (Timeless is a popular base) and trim or extend it. Keep a clear internal name so MediaWiki does not confuse your fork with the original
  • Community skinsCitizen (a modern, dark-mode capable skin) and Chameleon (Bootstrap-based, configuration-driven) cover many needs without custom code

Styling: ResourceLoader and LESS

ResourceLoader bundles, minifies and content-hashes assets, so browser caches are manageable without manual version bumping. During development, append ?debug=1 or set $wgResourceLoaderDebug = true; in LocalSettings.php to disable minification and caching. Core skins write stylesheets in LESS, and ResourceLoader compiles them on the fly; the newer approach is to expose design tokens as CSS custom properties so themes can be adjusted per-skin without recompiling.

Relative URLs inside stylesheets resolve against the stylesheet location, so images can be stored next to the CSS without absolute paths.

Testing a skin without disrupting users

A skin change can be previewed in three ways:

  • Per-userSpecial:Preferences → Appearance lets you test as another account
  • URL parameter — append ?useskin=myskin to any page (also works for logged-out users and is handy for screenshots)
  • Default$wgDefaultSkin switches everyone; keep it a deliberate, announced change

For deployments, remember $wgSkipSkins, which hides skins from the preference list — useful while a new skin is still experimental.

Common pitfalls

  • Template class mismatch — the $template property must exactly match the template class name, or MediaWiki fails with an "invalid skin template" error
  • Serving old CSS — after changing assets, modules are re-hashed automatically in production, but the page cache can still serve stale versions; purge the page or wait for the cache to expire
  • Overriding core modules — re-declaring a module name like mediawiki.util breaks dependents; always prefix your modules and skin idents with a unique name
  • Version compatibility — the skin system evolves with MediaWiki: one major upgrade introduced the templated SkinModule features flag, and 1.44 changed heading markup (skins must opt into supportsMwHeading for the new HTML). Always match the skin branch to your MediaWiki version
  • Mobile behavior — desktop skins are not automatically responsive-friendly; test with the ?useskin=minerva equivalent or check your layout at small widths early

Where to start

Before writing code, decide whether you are solving a looks problem or a workflow problem. For the former, MediaWiki:Common.css goes a long way; for the latter, forking Timeless or adopting Citizen is usually faster than a custom skin. When you do write one, the reference implementations in the skins/ folder of any MediaWiki install — Vector's template and modules in particular — are the best documentation there is, alongside Manual:Skinning on mediawiki.org.

Subscribe to MediaWiki Tips and Tricks

Don’t miss out on the latest articles. Sign up now to get access to the library of members-only articles.
jamie@example.com
Subscribe