Joomla 5 Extension Development Series: Components, Modules & Plugins (Admin → Frontend)
During the next weeks (or months :D), I'll publish a complete guide to developing Joomla 4/5/6 Components, Modules, and Plugins (for both frontend & backend), fully working and tested!
- - Part 1 - Build a Joomla 5 "Hello World" Administrator (backend) Component from scratch.
- - Part 2 - Extend the Admin Component with a Joomla 5 Frontend interface (Hello World).
- - Part 3 - (...soon...) Build a Joomla 5 "Hello World" Frontend Module from scratch.
- - Part 4 - (...soon...) Build a Joomla 5 "Hello World" Administrator (backend) Module from scratch.
- - Part 5 - (...soon...) Build a Joomla 5 "Hello World" Plugin (System/Content) from scratch.
Part 1:
Build a Joomla 5 “Hello World” Admin Component (from scratch)
This guide walks you through creating a minimal, modern Joomla 5 admin component using the current architecture: PSR-4 namespaces, a service provider (services/provider.php), and the MVC pattern. When you’re done, you’ll have an entry under Components → Hello World that renders a simple view — a perfect base to grow from. The steps and file layout follow Joomla’s official guidance.
Index
1.1 What you’ll build?
- - A component named com_helloworld with a single admin view.
- - Proper PSR-4 autoloading in the manifest. (Joomla will automatically map your Administrator and Site namespaces to the correct src folders).
- - A minimal service provider that wires the component into Joomla’s DI container.
1.2 Folder layout
Create this package outside of Joomla, then zip and install it. The structure matches Joomla’s expectations for admin components in 4/5.
com_helloworld/
├─ helloworld.xml
└─ admin/
├─ services/
│ └─ provider.php
├─ src/
│ ├─ Extension/
│ │ └─ HelloworldComponent.php
│ ├─ Controller/
│ │ └─ DisplayController.php
│ └─ View/
│ └─ Hello/
│ └─ HtmlView.php
└─ tmpl/
└─ hello/
└─ default.php
1.3 Manifest
The manifest declares your component, the PSR-4 namespace, and which files to install. The crucial line is:
<namespace path="src">…</namespace>
Joomla will derive two prefixes from this line and map them automatically:
- Vendor\Component\HelloWorld\Administrator\* → administrator/components/com_helloworld/src/*
- Vendor\Component\HelloWorld\Site\* → components/com_helloworld/src/*
This is official behavior in Joomla 4/5 (and preview of Joomla 6): https://manual.joomla.org/docs/next/general-concepts/namespaces/defining-your-namespace/?utm_source=chatgpt.com
<?xml version="1.0" encoding="utf-8"?>
<extension type="component" method="upgrade">
<name>com_helloworld</name>
<creationDate>2025-09-07</creationDate>
<author>Your Name</author>
<version>1.0.0</version>
<description>Hello World admin component for Joomla 5</description>
<!-- PSR-4: Joomla will map Administrator/Site to the src folder -->
<namespace path="src">Vendor\Component\Helloworld</namespace>
<administration>
<menu>Hello World</menu>
<files folder="admin">
<folder>services</folder>
<folder>src</folder>
<folder>tmpl</folder>
</files>
</administration>
</extension>
Why path="src" (and not admin/src)? Because the manifest’s path is the root inside the installed extension; Joomla will map Administrator/Site for you.
1.4 Service provider: admin/services/provider.php
This file registers your component in Joomla’s DI container and exposes the component Extension under ComponentInterface. It also registers dispatcher and MVC factories for your namespace. This is the officially recommended shape.
<?php
defined('_JEXEC') or die;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\CMS\Dispatcher\ComponentDispatcherFactoryInterface;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\Extension\Service\Provider\ComponentDispatcherFactory;
use Joomla\CMS\Extension\Service\Provider\MVCFactory;
use Vendor\Component\Helloworld\Administrator\Extension\HelloworldComponent;
return new class implements ServiceProviderInterface
{
public function register(Container $container): void
{
$namespace = '\\Vendor\\Component\\Helloworld';
$container->registerServiceProvider(new ComponentDispatcherFactory($namespace));
$container->registerServiceProvider(new MVCFactory($namespace));
$container->set(
ComponentInterface::class,
function (Container $container) {
$component = new HelloworldComponent(
$container->get(ComponentDispatcherFactoryInterface::class)
);
$component->setMVCFactory($container->get(MVCFactoryInterface::class));
return $component;
}
);
}
};
You could use MVCComponent directly (no custom Extension class) for ultra-minimal setups; both approaches are valid.
1.5 Extension class: admin/src/Extension/HelloworldComponent.php
This is your component’s Extension (admin side). For a “Hello World”, it simply extends MVCComponent.
<?php
namespace Vendor\Component\Helloworld\Administrator\Extension;
defined('_JEXEC') or die;
use Joomla\CMS\Extension\MVCComponent;
class HelloworldComponent extends MVCComponent
{
}
1.6 Controller: admin/src/Controller/DisplayController.php
The default controller sets the default view name.
<?php
namespace Vendor\Component\Helloworld\Administrator\Controller;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\Controller\BaseController;
class DisplayController extends BaseController
{
protected $default_view = 'hello';
}
1.7 View class: admin/src/View/Hello/HtmlView.php
The view prepares any data you want to pass into the layout.
<?php
namespace Vendor\Component\Helloworld\Administrator\View\Hello;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
class HtmlView extends BaseHtmlView
{
public string $greeting = 'Hello, World! 👋';
public function display($tpl = null)
{
return parent::display($tpl);
}
}
1.8 Layout: admin/tmpl/hello/default.php
This is the HTML template that renders in the admin UI.
<?php
<?php
defined('_JEXEC') or die;
?>
<div class=\"container\">
<h1>Hello World (Admin)</h1>
<p><?php echo \$this->greeting; ?></p>
<p>You can now replace this with your own PHP/HTML.</p>
</div>
1.9 Installation
- - Zip the com_helloworld/ folder to com_helloworld.zip.
- - In Joomla admin, go to System → Extensions → Install, upload the ZIP.
- - Navigate to Components → Hello World and you should see your page.
If you want to compare against a more complete reference, check the official Example Form Component.
1.10 How it works (and why it’s done this way)
- PSR-4 autoloading: Joomla parses installed manifests, collects all < namespace > entries and builds a map of namespace prefixes to directories. To avoid doing this on every request, it caches the result in administrator/cache/autoload_psr4.php. If you delete that file (or install/update an extension), Joomla regenerates it. This is why getting the < namespace > line right (and case-matching file paths) is critical.
- Service provider: admin/services/provider.php is the modern entry point that wires your component’s Extension and MVC factories into the DI container — this is explicitly documented in Joomla 4/5 docs.
- MVC flow: When you click Components → Hello World, Joomla boots your component via the service provider, dispatches the display task to DisplayController, loads the hello view, and renders tmpl/hello/default.php. For a deeper tour, see the Developing a Basic Component article (J4.x, still applicable conceptually).
1.11 Renaming the component later
Say you want com_adminhello instead, with namespace Vendor\Component\Adminhello and menu label Admin Hello:
On Manifest (helloworld.xml → adminhello.xml):
<name>com_adminhello</name>
<namespace path="src">Vendor\Component\Adminhello</namespace>
<menu>Admin Hello</menu>
On Service provider:
$namespace = '\Vendor\Component\Adminhello';
new AdminhelloComponent(...).
Classes & files (PSR-4 is case-sensitive):
src/Extension/AdminhelloComponent.php with namespace Vendor\Component\Adminhello\Administrator\Extension;
class AdminhelloComponent ...
If you rename the default view (e.g., to dashboard), change $default_view = 'dashboard', move src/View/Hello → src/View/Dashboard, and tmpl/hello → tmpl/dashboard.
1.12 Bonus nº1: Download & Install
Get the installable ZIP from the repository and upload it in Joomla (System → Extensions → Install): Download ZIP.
1.13 Bonus nº2: Python script to develop Joomla Admin Component
Once we understand the basic operation of this simple component, we can easily automate its creation by customizing certain parameters. Here's a simple Python script to give you an example of how we could streamline this type of task.
Here's the python script: https://github.com/nicolauroca/dev/blob/main/2025_PHP_Joomla_CreateAdminComponent.py
Example script usage:
python 2025_PHP_Joomla_CreateAdminComponent.py \
--name helloadminworld \
--path / \
--vendor NicolauRoca \
--menu HelloAdminWorld \
--author "NicolauRoca.dev" \
--version 1.0.0 \
--view hello
--zip
1.14 Bonus nº2: Preparing Your Joomla Extension for JED Submission
Once your Administrator Hello World component works, follow this checklist to make it ready for the Joomla Extensions Directory (JED). All points below are derived from official JED/Joomla docs and policies.
1) Naming rules (JED listing & manifest <name>)
- - Do not start the name with “Joomla”. If you use the word “Joomla” in the name, you need explicit authorization from OSM; the recommended pattern is “
MyProduct for Joomla”, not “Joomla MyProduct”. Source: JED: Extension Names and JED Entries Checklists. - - Avoid extension type keywords in the listing name. JED explicitly forbids “module” and “plugin” in a listing name; use a clear product name instead. Source: JED Submission Error Codes (NM3).
- - Keep the same name in your manifest XML <name> and in the JED listing. JED checks that the install name matches the listing title. Source: Extension Names, Error Codes (NM1).
2) Manifest: license tag is required
Your manifest must declare a license. JED requires GPL (GNU GPL) licensing for listed extensions.
- Add <license>…</license> to the manifest. Source: Error Codes (PH3/XM1).
- JED will only list extensions under the GNU GPL. Source: The GPL & the JED.
<license>GNU General Public License version 2 or later</license>
3) Manifest: update server is mandatory
All extensions submitted after 2017‑01‑10 must use the Joomla Update System (provide an update server in the manifest). Use the official tutorial to publish your update XML.
- - Requirement: JED - Joomla Update System Requirement
- - How to: Docs: Deploying an Update Server and Programmer’s Manual: Update Servers
<updateservers>
<server type="extension" priority="1" name="HelloWorld Update Server">https://example.com/updates/helloworld.xml</server>
</updateservers>
4) PHP file headers: GPL notice in every PHP file
Every .php file should start with a GPL notice (DocBlock) and block direct access.
- - GPL header required: Error Codes (PH1)
- - No direct access guard: Error Codes (PH2), Docs: Why files start with defined('_JEXEC')
<?php
/**
* @package HelloWorld
* @subpackage com_helloworld
* @copyright © 2025-09-12 Your Name
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
5) Nice‑to‑have (recommended)
- - Changelog URL in manifest:
<changelogurl>https://example.com/updates/changelog.xml</changelogurl>. Source: Docs: Adding changelog to your manifest. - - Download key support for paid updates:
<dlid>tag. Source: Docs: Manifest files (download keys). - - Language files: ship at least
en-GBlanguage packs for Administrator and Frontend. - - Packaging sanity: include a
LICENSE.txt, aREADME, and avoid obfuscated code or hidden backlinks (JEDChecker will flag them).
6) Minimal manifest example (with required tags)
<?xml version="1.0" encoding="utf-8"?>
<extension type="component" method="upgrade">
<name>HelloWorld</name>
<menu>Hello World</menu>
<creationDate>2025-09-12</creationDate>
<author>Your Name</author>
<authorEmail>you@example.com</authorEmail>
<authorUrl>https://your-site.com</authorUrl>
<version>1.0.0</version>
<license>GNU General Public License version 2 or later</license>
<description>A simple Hello World component</description>
<updateservers>
<server type="extension" priority="1" name="HelloWorld Update Server">https://your-site.com/updates/helloworld.xml</server>
</updateservers>
<namespace path="src">Vendor\Component\HelloWorld</namespace>
<files>
<filename>controller.php</filename>
<filename>router.php</filename>
<folder>views</folder>
</files>
</extension>
Tip: Run JED Checker locally before submitting. It validates license headers, JEXEC guards, manifest license tag, and update server presence. Source: JED Checker.
