Adding Custom Panel Categories

As you navigate the Product Menu, you can see that Panel Apps like Web Content and Site Settings are organized into Panel Categories such as Content and Configuration. This tutorial explains how to add new Panel Categories to the menu. Adding new Panel Apps is covered in the next section.

There are three steps to creating a new category:

  1. Create the OSGi structure and metadata.

  2. Implement Liferay’s Frameworks.

  3. Define the Control Menu Category.

Creating the OSGi Module

First you must create the project.

  1. Create an OSGi module using your favorite third party tool, or use Blade CLI. Blade CLI offers a Panel App template, which is for creating a panel category and panel app.

  2. Create a unique package name in the module’s src directory and create a new Java class in that package. To follow naming conventions, give your class a unique name followed by PanelCategory (e.g., ControlPanelCategory).

Implementing Liferay’s Frameworks

Next, you must connect your OSGi module to Liferay’s frameworks and use those to define information about your entry. This takes only two steps:

  1. Insert the @Component annotation declaring the panel category keys.

  2. Implement the PanelCategory interface.

Both of these steps are described below.

Insert the @Component Annotation

Directly above the class’s declaration, insert the following annotation:

        immediate = true,
        property = {
            "panel.category.key=" + [Panel Category Key],
        service = PanelCategory.class

The property element designates two properties that should be assigned for your category. The panel.category.key specifies the parent category for your custom category. You can find popular parent categories to assign in the PanelCategoryKeys class. For instance, if you wanted to create a child category in the Control Panel, you could assign PanelCategoryKeys.CONTROL_PANEL. Likewise, if you wanted to create a root category, like the Control Panel or Site Administration, you could assign PanelCategoryKeys.ROOT.

The panel.category.order:Integer property specifies the order in which your category is displayed. The higher the number (integer), the lower your category is listed among other sibling categories assigned to a parent.

Finally, your service element should specify the PanelCategory.class service. You can view an example of a similar @Component annotation for the UserPanelCategory class below.

        immediate = true,
        property = {
            "panel.category.key=" + PanelCategoryKeys.ROOT,
        service = PanelCategory.class

Implement the PanelCategory Interface

The PanelCategory interface requires you to implement the following methods:

  • getNotificationCount: returns the number of notifications to be shown in the panel category.
  • include: renders the body of the panel category.
  • includeHeader: renders the panel category header.
  • isActive: whether the panel is selected.
  • isPersistState: whether to persist the panel category’s state to the database. This saves the state of the panel category when navigating away from the menu.

You can reduce the number of methods you must implement if you extend a base class that already implements the PanelCategory interface. The recommended way to do this is by extending the BasePanelCategory or BaseJSPPanelCategory abstract classes. Typically, the BasePanelCategory is extended for basic categories (e.g., the Control Panel category) that only display the category name. To add more complex functionality, you can then provide a custom UI for your panel using any front-end technology by implementing the include() or includeHeader() from the PanelCategory interface.

If you plan to use JSPs as the front-end technology, extend a base class called BaseJSPPanelCategory that already implements the methods include() and includeHeader() for you. This is covered in more detail below.

Defining the Control Menu Category

After establishing the framework you’re using to create the category, you must add any other methods that are necessary to create your custom panel category. As you learned earlier, you can extend the BasePanelCategory and BaseJSPPanelCategory abstract classes to implement PanelCategory.


If you need something simple for your panel category like a name, extending BasePanelCategory is probably sufficient. For example, the ControlPanelCategory extends BasePanelCategory and specifies a getLabel method to set and display the panel category name.

    public String getLabel(Locale locale) {
        return LanguageUtil.get(locale, "control-panel");


If you need more complex functionality, extend BaseJSPPanelCategory and use JSPs to render the panel category. For example, the SiteAdministrationPanelCategory specifies the getHeaderJspPath and getJspPath methods. You could create a JSP with the UI you want to render and specify its path in methods like these:

    public String getHeaderJspPath() {
        return "/sites/site_administration_header.jsp";

    public String getJspPath() {
        return "/sites/site_administration_body.jsp";

One JSP renders the panel category’s header (displayed when panel is collapsed) and the other its body (displayed when panel is expanded).

You must also specify the servlet context from where you are loading the JSP files. If this is inside an OSGi module, make sure your bnd.bnd file has defined a web context path:

    Web-ContextPath: /my-module-web

Then reference the Servlet context using the symbolic name of your module like this:

        target = "(",
        unbind = "-"
    public void setServletContext(ServletContext servletContext) {

Excellent! You’ve successfully created a custom panel category to display in the Product Menu. In many cases, a panel category holds panel apps for users to access. You’ll learn about how to add a panel app to a panel category next.

« Customizing the Product MenuAdding Custom Panel Apps »
Was this article helpful?
0 out of 0 found this helpful