Skip to main content
Modules in Joomla
# Topics

Modules in Joomla

03 June 2026

Modules are the small boxes around the main content of a Joomla page: the menu, the login form, the breadcrumbs, the "latest articles" list, the footer.

They look simple in the backend, but underneath they touch positions, page assignment, access control, caching, and a clean structure you can even build yourself.

How Joomla builds, places, and assigns the small blocks around every page.

This article explains how Joomla modules really work. It starts with the basics for website owners and editors, moves on to the practical setup for administrators, and ends with the technical details for developers. You will learn what a module is, how it reaches the page, how to control where it shows, and how to avoid the most common mistakes.

The one idea to take home is this: a page has exactly one component in the middle, but as many modules as you like around it. Learn how one module is assembled, positioned, and assigned, and you can place, override, and build any of them.

1. The Basics

1.1 What is a Module?

A module is a small, reusable presentation block. It does not own a URL and it does not "run the page". It decorates the page that the component produced.

If the page were a newspaper, the component is the main article, and the modules are the sidebars, the weather box, the ads, and the footer.

1.2 Where Modules Sit in the Extension Family

A module is one of Joomla's five extension types. Knowing the neighbours is what makes the word "module" meaningful:

TypeRolePer pagePrefix
Component Main page content / application exactly 1 com_
Module Small boxes around the content many mod_
Plugin Event-driven behaviour in the background many plg_
Template Look, feel, and page layout (defines the positions) 1 site + 1 admin tpl_
Language Translations 1 active

A component answers the question "what is this page?". A module answers the question "what else should appear around it?".

1.3 The mod_ Naming Convention

Every module name starts with mod_. Unlike a component, a module has no option URL parameter, because it is never requested directly. The template decides where it appears, and Joomla decides whether it appears.

mod_menu            ← the site main menu
mod_login           ← login form
mod_articles_latest ← "latest articles" list
mod_breadcrumbs     ← breadcrumb trail
mod_custom          ← your own free HTML / text

You never link to a module. You publish it to a position and assign it to pages.

1.4 Modules You Already Use Every Day

Open a fresh Joomla 6 site and these site modules are already in the box:

ModulePurpose
mod_menu Renders a menu (the main navigation)
mod_login Front-end login / logout form
mod_breadcrumbs "You are here" trail
mod_custom Free-form HTML / text (the workhorse)
mod_articles_latest Most recent articles
mod_articles_popular Most-hit articles
mod_articles_category Articles from chosen categories
mod_banners Display banner ads from com_banners
mod_finder Smart Search box
mod_related_items Related articles by tag or keyword
mod_footer Copyright line
mod_languages Language switcher
mod_wrapper Embed another page in an <iframe>

The Administrator has its own set too: mod_menu, mod_toolbar, mod_title, mod_quickicon, mod_submenu, mod_latestactions, and more. The back-end interface is largely built from modules as well.

Back to top

2. Site versus Administrator Modules

2.1 Two Clients, One Concept

Modules live in two separate worlds, distinguished by a value called client_id:

public_html/
├── modules/                     ← SITE modules      (client_id = 0)
│   ├── mod_menu/
│   ├── mod_login/
│   └── mod_articles_latest/
└── administrator/
    └── modules/                 ← ADMIN modules     (client_id = 1)
        ├── mod_toolbar/
        ├── mod_quickicon/
        └── mod_submenu/
  • Site modules render in the front-end template's positions.
  • Administrator modules render in the back-end template, for example the Control Panel dashboards, the toolbar, and the page title.

It is the same architecture and the same file structure. Only the client and the template they plug into differ.

2.2 How a Module Reaches the Page

A module never receives the request. Joomla collects and renders it while the template is being built:

Browser request
   │  index.php?option=com_content&view=article&id=42
   ▼
CMS Application → Router → Component runs, produces the main content
   ▼
Template renders:  <jdoc:include type="component" />   ← the component output
                   <jdoc:include type="modules" name="sidebar-right" />
   ▼
For each module position the template asks for:
   ModuleHelper::getModules('sidebar-right')
      → load published modules for this position
      → filter by access level, language, and menu assignment
      → render each via its Dispatcher + layout
   ▼
Template wraps everything → page sent to browser

The template's <jdoc:include type="modules" name="..."> tags are the sockets. Modules are what you plug into them.

Back to top

3. Positions and Assignment

This is the heart of using modules. Two settings decide where a module shows on the screen and on which pages it appears.

3.1 Module Positions

A position is a named slot defined by the template, not by Joomla core. The template lists its positions in its templateDetails.xml:

<positions>
    <position>sidebar-left</position>
    <position>sidebar-right</position>
    <position>banner</position>
    <position>footer</position>
</positions>
  • A module is published to one position, for example sidebar-right.
  • Multiple modules in the same position stack by their ordering.
  • Switch the template and the same module may need a new position name.

Tip: Append ?tp=1 to any front-end URL (with Preview Module Positions enabled in the template options) to see every position outlined on the live page.

3.2 Menu Assignment - Where a Module Shows

This is the feature people miss most often. Each module has a Menu Assignment setting:

SettingMeaning
On all pages Show everywhere
No pages Effectively disabled (still published)
Only on the pages selected Show on the chosen menu items
On all pages except those selected Show everywhere but the chosen menu items

Joomla stores this in the #__modules_menu table:

#__modules_menu
moduleid │ menuid
─────────┼────────
   16    │   0      ← menuid 0  = "all pages"
   42    │   8      ← show only on menu item 8
   42    │   12     ← ...and menu item 12
   55    │  -8      ← negative = "all pages EXCEPT menu item 8"

One module row can have many assignment rows. The sign of menuid encodes include versus exclude.

3.3 The Other Show and Hide Filters

Beyond menu assignment, three more conditions decide if a module renders:

FilterColumnControls
Published published On or off
Access level access Which viewing-access group may see it (Public, Registered, ...)
Language language * = all, or a specific language on multilingual sites
Status and dates publish_up, publish_down Time-boxed visibility window

A module renders only when all of these pass and its position is included by the template.

3.4 Show Title and the "Module Style" (Chrome)

Two presentation choices wrap every module:

  • Show Title (showtitle): render the module's title above its content, or not.
  • Module Style (chrome): the HTML wrapper around the module, provided by the template, for example card, well, none, or html5. You set it per module under Advanced → Module Style.
Chrome "card":           Chrome "none":
┌─────────────────┐
│  Title          │     (just the raw module output,
│ ─────────────── │      no wrapper, no title)
│  module output  │
└─────────────────┘

Chrome is why the same module can look like a boxed card in one template and a bare list in another.

Back to top

4. Inside a Module (Developer View)

4.1 The Modern Joomla 4/5/6 Folder Layout

Modules are "MVC-lite". There is no Controller and no Model class. Instead a module has a Dispatcher, a Helper, and a layout:

modules/mod_articles_latest/
├── mod_articles_latest.xml      ← manifest (name, version, files, params)
├── services/
│   └── provider.php             ← DI service registration (entry point)
├── src/
│   ├── Dispatcher/
│   │   └── Dispatcher.php        ← prepares the data for the layout
│   └── Helper/
│       └── ArticlesLatestHelper.php  ← the actual data fetching / logic
├── tmpl/
│   └── default.php               ← the HTML layout
└── language/                     ← translation .ini files
    └── en-GB/
        ├── mod_articles_latest.ini
        └── mod_articles_latest.sys.ini

Since Joomla 4 there is no mod_xxx.php entry file at the root. The entry point is services/provider.php, exactly like a component.

4.2 The Manifest (mod_articles_latest.xml)

<extension type="module" client="site" method="upgrade">
    <name>mod_articles_latest</name>
    <version>3.0.0</version>
    <description>MOD_LATEST_NEWS_XML_DESCRIPTION</description>
    <namespace path="src">Joomla\Module\ArticlesLatest</namespace>

    <files>
        <folder module="mod_articles_latest">services</folder>
        <folder>src</folder>
        <folder>tmpl</folder>
    </files>

    <config>
        <fields name="params"> ... the module's Options form ... </fields>
    </config>
</extension>

The key attributes are:

  • type="module" and client="site" (or client="administrator").
  • <namespace> registers PSR-4 autoloading for src/.
  • <config><fields name="params"> defines the form you see in the module editor. Joomla stores the result as JSON in #__modules.params.

4.3 The Entry Point (services/provider.php)

Like components, modules register themselves with the Dependency Injection (DI) container:

return new class () implements ServiceProviderInterface {
    public function register(Container $container)
    {
        $container->registerServiceProvider(
            new ModuleDispatcherFactory('\\Joomla\\Module\\ArticlesLatest')
        );
        $container->registerServiceProvider(
            new HelperFactory('\\Joomla\\Module\\ArticlesLatest\\Site\\Helper')
        );
        $container->registerServiceProvider(new Module());
    }
};
  • ModuleDispatcherFactory builds the module's Dispatcher.
  • HelperFactory builds Helper objects, with the database injected.
  • Module() is the generic module service that ties it together.

There is no manual require and no global state. It is the same modern wiring as a component, just lighter.

4.4 The Dispatcher - Prepare the Data

The Dispatcher has one job: gather the data the layout needs and hand it over.

class Dispatcher extends AbstractModuleDispatcher implements HelperFactoryAwareInterface
{
    use HelperFactoryAwareTrait;

    protected function getLayoutData()
    {
        $data = parent::getLayoutData();   // $data['params'], $data['module'], $data['app']

        $data['list'] = $this->getHelperFactory()
            ->getHelper('ArticlesLatestHelper')
            ->getArticles($data['params'], $this->getApplication());

        return $data;                      // becomes variables in tmpl/default.php
    }
}

Whatever keys you put in $data become $list, $params, $module, and so on inside the layout.

4.5 The Helper - The Logic

The Helper does the real work. Here it queries articles. It receives the database through dependency injection:

class ArticlesLatestHelper implements DatabaseAwareInterface
{
    use DatabaseAwareTrait;

    public function getArticles(Registry $params, SiteApplication $app)
    {
        // Reuse the component's own model - do not reinvent the query:
        $model = $app->bootComponent('com_content')
                     ->getMVCFactory()
                     ->createModel('Articles', 'Site', ['ignore_request' => true]);

        $model->setState('list.start', 0);
        $model->setState('list.limit', (int) $params->get('count', 5));
        // ... apply category, ordering, author filters from $params ...

        return $model->getItems();
    }
}

A good module is thin: it leans on the component's model for data and only handles presentation concerns.

4.6 The Layout (tmpl/default.php)

This is pure presentation. The variables come straight from the Dispatcher's $data:

<?php defined('_JEXEC') or die;

if (!$list) {
    return;                       // nothing to show → render nothing
}
?>
<ul class="mod-articleslatest mod-list">
<?php foreach ($list as $item) : ?>
    <li>
        <a href="/<?php echo $item->link; ?>">
            <?php echo htmlspecialchars($item->title, ENT_QUOTES, 'UTF-8'); ?>
        </a>
    </li>
<?php endforeach; ?>
</ul>

A module can ship multiple layouts in tmpl/, for example default.php and _item.php. Joomla stores the chosen one in the module's layout param.

Back to top

5. The Database

5.1 #__modules - One Row per Placed Module

Every module instance you create in the back-end is one row in this table:

ColumnMeaning
id Unique instance id
title The instance's name ("Main Menu", "Login Form")
module Which module type: mod_menu, mod_login, ...
position Template position it renders in (sidebar-right)
ordering Stacking order within the position
published -2 trashed / 0 unpublished / 1 published
access Viewing access level
showtitle Show the title or not
params JSON: the module's Options (count, category, layout, cache, ...)
client_id 0 = site, 1 = administrator
language * or a specific language
publish_up / publish_down Scheduled visibility window

Note the distinction: the module column is the type (one per installed extension), and the id is one instance. You can place many "Custom HTML" modules - many rows, all with module = mod_custom.

5.2 Type versus Instance

#__extensions                       #__modules
(one row per installed type)        (one row per placed instance)
─────────────────────────       ───────────────────────────────
type=module, element=mod_custom  ←  id=20  module=mod_custom  position=footer
                                    id=33  module=mod_custom  position=sidebar
                                    id=41  module=mod_custom  position=banner
  • #__extensions holds the installed module code (enabled state, default params).
  • #__modules holds each placement of that code on the site.

5.3 #__modules_menu - The Assignment Map

As shown in section 3.2, this table holds one row per (module, menu item) pair, with sign-encoded include and exclude:

-- Which pages does module 42 appear on?
SELECT menuid FROM #__modules_menu WHERE moduleid = 42;
--  0      → all pages
--  8, 12  → only these menu items
-- -8      → all pages except menu item 8

Together, #__modules and #__modules_menu answer "what, where, and on which pages" for every block on the site.

5.4 ACL - Modules and the Asset Tree

Modules plug into the central Access Control List (ACL) like everything else. Each module has an asset_id that points into #__assets. The relevant actions are:

ActionMeaning
core.edit Edit any module
core.edit.state Publish, unpublish, or order
core.edit.own Edit modules you created
core.delete Delete modules
core.create Create new modules

You set these permissions in Modules → Options → Permissions (component-wide) and you can override them per module. On top of that, the access column controls viewing: which front-end groups actually see the rendered module.

Back to top

6. Working with Modules

6.1 Creating and Placing a Module

The standard workflow is:

  1. Go to System → Manage → Modules (or Site Modules / Administrator Modules).
  2. Click New and pick a module type (mod_custom, mod_menu, ...).
  3. Give it a title and choose Show Title.
  4. Set the Position: type or pick a template position.
  5. Set Menu Assignment: all, none, selected, or all-except.
  6. Set Status, Access, Language, and optional publish dates.
  7. Configure the type-specific Options (the params form from the manifest).
  8. Save. Joomla writes a new row in #__modules.

6.2 Overriding a Module's Output the Right Way

Never edit core module files. Joomla overwrites them on update. Use a template override instead:

Copy:  modules/mod_articles_latest/tmpl/default.php
To:    templates/<your-template>/html/mod_articles_latest/default.php
NeedCorrect technique
Change the HTML output Template override in html/mod_xxx/
Change the title wrapper Module chrome / style (override in html/layouts/)
Change behaviour Often better as a plugin or a custom module
Change strings Language override (System → Languages → Overrides)

Overrides survive updates and let you restyle a module without forking its code.

6.3 mod_custom - The Swiss-army Module

This is the single most useful core module: free HTML or text with the editor.

  • Drop in a promo banner, a snippet of script, an embedded map, or contact details.
  • Combine it with menu assignment for page-specific content.
  • Enable Prepare Content in its options so content plugins run inside it, for example {loadposition} style shortcodes or custom-fields output.

When in doubt, a mod_custom plus a template override solves a huge share of "I need a box here that says X" requests.

6.4 Loading a Module Inside Content

Sometimes you want a module within an article, not in a sidebar position:

TechniqueWhereWhat it does
loadposition in an article Render all modules in position mypos
loadmodule Render one specific module
<jdoc:include> In a template Render a position
ModuleHelper::renderModule() In PHP Render a module object programmatically

The {load...} shortcodes are provided by the Content - Load Modules plugin, which must be enabled.

Back to top

7. Performance and Advanced Topics

7.1 Module Caching

Most core modules expose cache settings in their Advanced options, defined in the manifest:

<field name="cache" type="list" default="1">
    <option value="1">JGLOBAL_USE_GLOBAL</option>     <!-- use global cache -->
    <option value="0">COM_MODULES_FIELD_VALUE_NOCACHING</option>
</field>
<field name="cache_time" type="number" default="900" />   <!-- seconds -->
<field name="cachemode" type="hidden" default="static" />
Cache modeCaches per...Use when
static Module id (one cache for everyone) Output is the same for all visitors
itemid Menu item Output varies by page
safeuri Selected URL params Output varies by query string
(none) Never cache Per-user output (login, who's online)

Warning: A login or "who's online" module must not be statically cached. It would show one user's state to everyone.

7.2 Module Ordering and Multiple Instances

  • Within a position, modules render in ordering sequence. Drag to reorder them in the list view.
  • You can place many instances of the same type with different params, for example three mod_custom boxes or two menus.
  • Batch processing in the Modules list lets you set position, access, or language on many modules at once.

7.3 Building Your Own Module - The Minimum

A bare-bones working site module looks like this:

mod_hello/
├── mod_hello.xml                       ← manifest (type="module" client="site")
├── services/provider.php               ← register Dispatcher + Helper + Module
├── src/
│   └── Dispatcher/Dispatcher.php        ← (Helper optional for trivial modules)
├── tmpl/default.php                    ← <h3>Hello, JUG!</h3>
└── language/en-GB/
    ├── mod_hello.ini
    └── mod_hello.sys.ini
  1. Zip it and install via System → Install → Extensions.
  2. Joomla reads the manifest, copies the files, and registers the namespace.
  3. Create a module instance, set a position, assign it to pages, and it renders.

Tip: Start by copying mod_custom (the simplest core module) and renaming everything consistently.

7.4 Module versus Component - When to Build Which

Build a module when...Build a component when...
You need a small, repeatable block You need a full page or application
It decorates pages the component made It owns the URL and the main content
No own URL, no detail views It has list and detail views, its own routing
Mostly presentation plus a query It manages data, ACL per item, an admin UI

Many real extensions ship both in one package (pkg_): a component for management plus modules to surface its data.

7.5 Security - A Module Renders Untrusted Data

A module sits on a public page and often echoes user-supplied or database content, so it is a classic injection surface. Four rules:

RuleWhyHow
Escape on output Stop XSS in titles, params, query results htmlspecialchars($v, ENT_QUOTES, 'UTF-8') in the layout
Validate / filter input Never trust request or param values Use filter attributes in the manifest (intarray, integer, CssIdentifier, ...)
No string-concatenated SQL Prevent SQL injection in Helpers Use the query builder plus bound or quoted params
Respect ACL and access Do not leak privileged data Honour the module's access level and check authorise() for sensitive output
// Layout - always escape:
echo htmlspecialchars($item->title, ENT_QUOTES, 'UTF-8');

// Helper - bind, never concatenate:
$query->where($db->quoteName('catid') . ' = :cat')
      ->bind(':cat', $catid, ParameterType::INTEGER);

The manifest's filter="..." on each field is your first line of defence. Values are sanitised before they ever reach params.

Back to top

8. Debugging Modules

8.1 The Module-debugging Toolkit

When a module misbehaves (wrong place, no output, stale content), work through these in order:

TechniqueReveals
Preview positions (?tp=1) Whether the position exists in the active template and what it is named
System → Global Config → Debug Joomla Debug plus the Profile / queries panel - see the module's queries and timing
Inspect rendered HTML The chrome wrapper, CSS classes, and whether any output was produced at all
Check the database rows The real position, published, access, language, and assignment
Review template overrides An old override in html/mod_xxx/ silently winning over the core layout

8.2 The Three Tables to Check

-- Is it published, where, for whom?
SELECT id, title, position, published, access, language
FROM #__modules WHERE module = 'mod_articles_latest';

-- Which pages is it assigned to? (0=all, negative=except)
SELECT moduleid, menuid FROM #__modules_menu WHERE moduleid = 42;

-- Is the extension itself enabled?
SELECT element, enabled FROM #__extensions
WHERE type = 'module' AND element = 'mod_articles_latest';

The classic "module won't appear" checklist is: published?right position for this template?assigned to this page?access level matches the visitor?language matches? Nine times out of ten it is one of those five.

Back to top

9. Common Mistakes and Pitfalls

9.1 The Module Does Not Show

  • Wrong position name for the active template. The module is published but silently does not show.
  • Forgetting Menu Assignment, so the module appears everywhere or nowhere by accident.
  • Access level mismatch, where the module is set to a level the visitor does not belong to.
  • Language mismatch on a multilingual site, where the module is bound to one language only.

9.2 Caching Mistakes

  • Static caching a per-user module (login, who's-online), which leaks one visitor's state to everyone.
  • Stale content because the cache time is too long for content that changes often.

9.3 Developer and Editor Gotchas

  • Editing core module files, which are lost on update. Use template overrides instead.
  • Confusing the type (the module column) with the instance (the id).
  • Putting heavy logic in the layout instead of in the Helper.
  • Echoing titles or params unescaped, which opens the door to XSS.
Back to top

10. Best Practices

If you only remember a few things from this article, remember these:

  • A module is a small presentation block. There can be many per page, and the name always starts with mod_.
  • A module has no URL. The template's <jdoc:include type="modules"> sockets and the module's position decide where it appears.
  • Menu assignment, access, language, status, and dates decide whether it appears.
  • Keep modules thin: the Dispatcher prepares data, the Helper does the logic, the layout renders HTML.
  • Override output safely with template overrides, never by editing core files.
  • Escape output, filter input, and bind SQL, because a module renders untrusted data on a public page.
Back to top

11. Quick Reference

CREATE       System → Manage → Modules → New
POSITION     Pick a template position (sidebar-right, ...)
ASSIGN       Menu Assignment: all / none / selected / except
SHOW         Status: Published + Access matches the visitor
PREVIEW      Append ?tp=1 to a front-end URL to see positions
OVERRIDE     templates/{tpl}/html/mod_xxx/default.php
IN CONTENT    or 
CACHE        static (all visitors) vs none (per-user modules)
DEBUG        #__modules + #__modules_menu + #__extensions
BUILD        Copy mod_custom, rename, register in provider.php
Back to top

12. Summary

In Joomla, modules are the quiet building blocks around your content. They influence almost every part of the visible page:

  • Navigation: the menus and breadcrumbs visitors use.
  • Layout: the sidebars, banners, and footer that frame the content.
  • Targeting: which pages and which user groups see each block.
  • Performance: caching that keeps pages fast without leaking user state.
  • Reuse: a clean Dispatcher, Helper, and layout structure you can copy and extend.

Once you understand how one module is assembled, positioned, and assigned, you can place, override, and build any module on the site. When a module refuses to show, the answer is almost always in the five-point checklist: published, position, assignment, access, language.

If you are planning a new Joomla site, building a custom module, or you are stuck with a module that will not appear where you expect, it pays to understand this architecture early. Modules are simple to use, but the details underneath are where good Joomla sites are made.

Back to top
Modules in Joomla
Peter Martin

Joomla en Linux specialist voor snelle, veilige en schaalbare websites.