Control Panel Section

Modules and plugins can add new sections to the Control Panel using the EVENT_REGISTER_CP_NAV_ITEMS event:

use craft\events\RegisterCpNavItemsEvent;
use craft\web\twig\variables\Cp;
use yii\base\Event;

public function init()
{
    parent::init();

    Event::on(
        Cp::class,
        Cp::EVENT_REGISTER_CP_NAV_ITEMS,
        function(RegisterCpNavItemsEvent $event) {
            $event->navItems[] = [
                'url' => 'section-url',
                'label' => 'Section Label',
                'icon' => '@ns/prefix/path/to/icon.svg',
            ];
        }
    );

    // ...
}

Each item within the navItems array can have the following keys:

  • url – The URL that the nav item should link to. (It will be run through craft\helpers\UrlHelper::cpUrl().)
  • label – The user-facing nav item label.
  • icon – The path to the icon SVG that should be used. (It can begin with an alias.)
  • badgeCount(optional) – The badge count that should be displayed in the nav item.
  • subnav(optional) – An array of subnav items that should be visible when your section is accessed. (See Subnavs.)

If your section has a sub-navigation, each subnav item within your subnav array should be represented by a sub-array with url and label keys:

'subnav' => [
    'foo' => ['label' => 'Foo', 'url' => 'section-url/foo'],
    'bar' => ['label' => 'Bar', 'url' => 'section-url/bar'],
    
    // A subnav can also have it's own badge count by adding the optional `badgeCount` key:
    'baz' => ['label' => 'Baz', 'url' => 'section-url/baz', 'badgeCount' => 5],
],

Your templates can specify which subnav item should be selected by setting a selectedSubnavItem variable to the key of the nav item:

{% set selectedSubnavItem = 'bar' %}

Plugin Sections

Plugins that only need to add one section can set the $hasCpSection property on their primary plugin class, rather than using the EVENT_REGISTER_CP_NAV_ITEMS event:

<?php

namespace ns\prefix;

class Plugin extends \craft\base\Plugin
{
    public $hasCpSection = true;

    // ...
}

TIP

You can alternatively set a hasCpSection value in composer.json as noted in the plugin guide. We don’t recommend setting it in both places, however, since the value set in composer.json will override your public class property’s value and likely create confusion.

You can modify aspects of the plugin’s Control Panel nav item by overriding its getCpNavItem() method:

public function getCpNavItem()
{
    $item = parent::getCpNavItem();
    $item['badgeCount'] = 5;
    $item['subnav'] = [
        'foo' => ['label' => 'Foo', 'url' => 'plugin-handle/foo'],
        'bar' => ['label' => 'Bar', 'url' => 'plugin-handle/bar'],
        'baz' => ['label' => 'Baz', 'url' => 'plugin-handle/baz'],
    ];
    return $item;
}

If you do this, Craft will automatically add a new user permission for your plugin, and only show the nav item for users that have it.

Clicking on a plugin’s section will take the user to /admin/plugin-handle, which will attempt to load an index.html or index.twig template within the plugin’s template root (its templates/ folder within its base source folder).

TIP

See Control Panel Templates for more information about developing Control Panel templates.

Alternatively, you can route /admin/plugin-handle requests to a controller action (or a different template) by registering a Control Panel route from your plugin’s init() method:

use craft\events\RegisterUrlRulesEvent;
use craft\web\UrlManager;
use yii\base\Event;

public function init()
{
    Event::on(
        UrlManager::class,
        UrlManager::EVENT_REGISTER_CP_URL_RULES,
        function(RegisterUrlRulesEvent $event) {
            $event->rules['plugin-handle'] = 'plugin-handle/foo/bar';
        }
    );
}