So I find myself needing to write a module for Joomla! and thought I would document it as I go so that I have something to refer to in the future. Perhaps you'll find it useful too.
TL:DR – There are lots of resources for Joomla on the Internet going back decades and in many cases not that useful. The most useful I've found is the Official Joomla Module Development Tutorial. These are my specific notes based on that tutorial about writing a module for Joomla 5.x for myself which I've documented for my own needs in late 2024.
Contents
A module called mod_modulename
Once completed this will provide the skeleton/boilerplate for a real module I need.
Folder Structure
Joomla appears to require a very specific structure for the folders for your module.
mod_modulename
  ├── mod_modulename.xml
  ├── services
  │   └── provider.php
  └── src 
      └── Dispatcher 
          └── Dispatcher.phpManifest file
<?xml version="1.0" encoding="UTF-8"?>
<extension type="module" client="site" method="upgrade">
    <name>Module name</name>
    <version>1.0</version>
    <author>Your Name</author>
    <creationDate>07 Oct 2024</creationDate>
    <description>Your first Joomla module</description>
    <namespace path="src">YourCompanyName\Module\ModuleName</namespace>
    <files>
        <folder module="mod_modulename">services</folder>
        <folder>src</folder>
    </files>
</extension>
This manifest tells the Joomla installer everything it needs to know about the extension
- <extension- type="module"- client="site"- method="upgrade">— the extension type is a module, for the front-end and it can safely upgrade an existing version of itself.
- <name>,- <author>,- <creationdate>,- <description>— are all text fields visible in Joomla.
- <version>— version of the module.
- <files>— tells the installer which files should be considered part of the module code. The- module="mod_modulename"attribute tells Joomla to look in the /services folder to find a service provider file which is the starting point of mod_hello.
- <namespace> — is the namespace prefix for our module mod_modulename. Im usingMyCompanyName\Module\ModuleName. I assume this is aimed at avoiding collisions of naming with other developers although theres no enforcement or central registry for that.
Service Provider code
<?php
\\defined('_JEXEC') or die;
use Joomla\CMS\Extension\Service\Provider\Module as ModuleServiceProvider;
use Joomla\CMS\Extension\Service\Provider\ModuleDispatcherFactory as ModuleDispatcherFactoryServiceProvider;
use Joomla\CMS\Extension\Service\Provider\HelperFactory as HelperFactoryServiceProvider;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
return new class () implements ServiceProviderInterface {
    public function register(Container $container): void
    {
        $container->registerServiceProvider(new ModuleDispatcherFactoryServiceProvider('\\My\\Module\\Hello'));
        $container->registerServiceProvider(new HelperFactoryServiceProvider('\\My\\Module\\Hello\\Site\\Helper'));
        $container->registerServiceProvider(new ModuleServiceProvider());
    }
};
The official site says "If you're new to Joomla development then this code probably looks very intimidating. If so, the best thing is just to accept it for now." OK then, we will do that.
Dispatcher
<?php
namespace YourCompanyName\Module\ModuleName\Site\Dispatcher;
\defined('_JEXEC') or die;
use Joomla\CMS\Dispatcher\DispatcherInterface;
class Dispatcher implements DispatcherInterface
{
    public function dispatch()
    {
        echo '<h4>Hello, World</h4>';
    }
When Joomla runs the module code it starts by instantiating our Dispatcher class and calling its dispatch() function.
Install your module
- Zip up the folder using % zip -r mod_modulename.zip mod_modulename/.
- Install via Extensions > Install in the Joomla Console


Enabling your new module
- You can now find your new module un Content > Site Modules > New.
- A module will be created in the site modules list, but it will not be published.
- You'll need to select a module position for it or create a new one.
- Select which pages it is eligible to display on.

The module
If the module is working you should see the text 'Hello' below
Hello Guest
Testing
The authors of the Joomla Developer Manuals included automated tests for the module. This is really nice, so I thought it best to adopt them for my module too.
npx cypress run
It looks like this is your first time using Cypress: 13.13.2
✔  Verified Cypress! ..../Cypress.app
Opening Cypress...
DevTools listening on ws://127.0.0.1:51117/devtools/browser/xxxxxxxxx-4531-43c6-bc0f-xxxxxxxxx
====================================================================================================
  (Run Starting)
  ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ Cypress:        13.13.2                                                                        │
  │ Browser:        Electron 118 (headless)                                                        │
  │ Node Version:   v22.5.1 (/opt/homebrew/Cellar/node/22.5.1/bin/node)                            │
  │ Specs:          1 found (tests.cy.js)                                                          │
  │ Searched:       cypress/integration/tests.cy.js                                                │
  └────────────────────────────────────────────────────────────────────────────────────────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────────
                                                                                                    
  Running:  tests.cy.js                                                                     (1 of 1)
  Testing 'Joomla module tutorial' from '../module-tutorial'
    Testing 'step1_basic_module'
      ✓ Uninstall extension, if existing (2885ms)
      ✓ Install extension 'Joomla module tutorial' (10618ms)
      ✓ Check module exists in administrator backend (291ms)
      ✓ Check module outputs on the frontend website (1801ms)