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 2:
Extend the Admin Component with a Joomla 5 Frontend interface (Hello World)
This guide walks you through creating a minimal, modern Joomla 5 admin + frontend 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.
Important: This article continues from "Part 1" taking the admin-only component built there and extending it with a Site layer and a Menu Item Type for the frontend.
Index
1.1 Overview (what we’re going to do)
- - Add a minimal Site MVC (controller + view + layout).
- - Expose a Menu Item Type using a layout metadata file (default.xml) so the view appears in the “Select Menu Item Type” dialog.
- - Add the language files needed for both Site and Administrator contexts.
- - Update the manifest so Joomla installs the Site side and the languages.
- - Run quick smoke tests and cover the most common pitfalls.
1.2 What you’ll have at the end
- A working frontend route you can open directly: /index.php?option=com_hellonico&view=hello
- A Menu Item Type (“Hello (frontend)”) you can pick when creating a menu item.
- A simple menu parameter (Greeting) that the view reads and displays.
- A clean structure and namespaces ready for more models, views, and even a custom router later.
1.3 Create the Site layer (minimal MVC)
Add these folders and files to the package:
site/
├─ src/
│ ├─ Controller/
│ │ └─ DisplayController.php
│ └─ View/
│ └─ Hello/
│ └─ HtmlView.php
└─ tmpl/
└─ hello/
├─ default.php
└─ default.xml
default.xml is the key that makes your view available as a Menu Item Type. Joomla detects this XML when it sits next to your layout (default.php). That’s how your view shows up in “Menus → New → Select Menu Item Type”.
Example: site/tmpl/hello/default.xml
<?xml version="1.0" encoding="utf-8"?>
<metadata>
<layout title="COM_HELLONICO_MENU_HELLO_TITLE">
<message>COM_HELLONICO_MENU_HELLO_MSG</message>
</layout>
<fields name="params">
<fieldset name="basic">
<field name="greeting" type="text"
label="COM_HELLONICO_FIELD_GREETING_LABEL"
description="COM_HELLONICO_FIELD_GREETING_DESC"
default="Hello from frontend!" />
</fieldset>
</fields>
</metadata>
Example: site/src/Controller/DisplayController.php
<?php
namespace Nico\Component\Hellonico\Site\Controller;
\defined('_JEXEC') or die;
use Joomla\CMS\MVC\Controller\BaseController;
class DisplayController extends BaseController
{
protected $default_view = 'hello';
}
Example: site/src/View/Hello/HtmlView.php
<?php
namespace Nico\Component\Hellonico\Site\View\Hello;
\defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Factory;
class HtmlView extends BaseHtmlView
{
protected $greeting;
public function display($tpl = null)
{
$app = Factory::getApplication();
$menu = $app->getMenu()->getActive();
$params = $menu ? $menu->getParams() : $app->getParams();
$this->greeting = $params->get('greeting', 'Hello from frontend!');
return parent::display($tpl);
}
}
Heads-up: if you ever see “Invalid controller class: display”, your namespaces are off. The controller must be under Nico\Component\Hellonico\Site\Controller\DisplayController and views under Nico\Component\Hellonico\Site\View\… (PSR-4 is case-sensitive).
1.4 Add language files (and the crucial .sys.ini)
Create Site languages for strings used inside your frontend view, and Administrator .sys.ini files for strings that Joomla’s admin UI needs (for example, the Menu Item Type title and description shown in the selector). Use one consistent prefix aligned with your component element. If your element is com_hellonico, always use COM_HELLONICO_*.
Administrator system language files (used by the menu selector and other admin UI):
- - administrator/language/en-GB/en-GB.com_hellonico.sys.ini
- - administrator/language/es-ES/es-ES.com_hellonico.sys.ini
Example content:
- - COM_HELLONICO_MENU_HELLO_TITLE="Hello (frontend)"
- - COM_HELLONICO_MENU_HELLO_MSG="Displays the Hello view on the frontend."
Site language files (used inside the frontend view/layout):
- - site/language/en-GB/en-GB.com_hellonico.ini
- - site/language/es-ES/es-ES.com_hellonico.ini
Example content:
- - COM_HELLONICO_FIELD_GREETING_LABEL="Greeting"
- - COM_HELLONICO_FIELD_GREETING_DESC="Optional greeting to display."
1.5 Update the manifest to install the Site side and languages
In com_hellonico.xml, declare the Site folders and languages, and make sure the Administrator languages are also copied.
Example snippet:
<extension type="component" method="upgrade">
<name>com_hellonico</name>
<namespace path="src">Nico\Component\Hellonico</namespace>
<files folder="site">
<folder>src</folder>
<folder>tmpl</folder>
<folder>language</folder>
</files>
<languages>
<language tag="en-GB">site/language/en-GB/en-GB.com_hellonico.ini</language>
<language tag="es-ES">site/language/es-ES/es-ES.com_hellonico.ini</language>
</languages>
<administration>
<files folder="admin">
<folder>services</folder>
<folder>src</folder>
<folder>tmpl</folder>
<folder>language</folder>
</files>
<languages>
<language tag="en-GB">admin/language/en-GB/en-GB.com_hellonico.sys.ini</language>
<language tag="es-ES">admin/language/es-ES/es-ES.com_hellonico.sys.ini</language>
</languages>
</administration>
</extension>
Remember: the manifest controls what gets copied on install. If a folder is not declared, Joomla will not install it. Ensure site/language and admin/language are included (via
1.6 Confirm the Service Provider (bootstrap)
Keep admin/services/provider.php as the DI entry point. There you:
- - Set the base namespace for your component.
- - Register the ComponentDispatcherFactory and the MVCFactory for that namespace.
- - Optionally register a RouterFactory if you later add a custom Site\Service\Router.
If you rename the vendor or namespace, update both the provider’s internal namespace string and the import of your Extension class so everything matches. After renaming, reinstall the package or delete administrator/cache/autoload_psr4.php to rebuild the autoload map.
1.7 Smoke test (and quick troubleshooting)
Install or update your ZIP.
Create a Menu Item and click Select to choose a Menu Item Type. You should see the type that default.xml exposes (“Hello (frontend)”). If you see raw COM_* keys there, your Administrator .sys.ini keys are missing or the prefix is wrong; clear cache and try again.
Open the non-SEF URL to isolate routing problems:
/index.php?option=com_hellonico&view=hello
If this works but your SEF alias does not, check .htaccess rewrite/SEF settings or multilingual configuration. If you get “Invalid controller class: display”, fix the Site\Controller namespace or path (PSR-4 is case-sensitive).
1.8 Final structure
com_hellonico/ — component package root.
├─ com_hellonico.xml — manifest: declares name, namespace, install paths, languages, and (optionally) update servers.
├─ LICENSE — package license (e.g., GPL); not executed by Joomla.
├─ README.md — component notes/guide; informational.
├─ admin/ — component backend (Administrator application) code.
│ ├─ services/ — dependency-injection bootstrap for the extension.
│ │ └─ provider.php — ServiceProvider: registers dispatcher/MVC/router for the admin side.
│ ├─ src/ — backend PHP classes (PSR-4).
│ │ ├─ Extension/ — admin-side extension classes (component entrypoint).
│ │ │ └─ HellonicoComponent.php — main component class for admin (bootstrap/behaviour).
│ │ ├─ Controller/ — admin MVC controllers.
│ │ │ └─ DisplayController.php — default admin controller; routes to the view.
│ │ └─ View/ — admin MVC views.
│ │ └─ Hello/
│ │ └─ HtmlView.php — admin HTML view; prepares data/params for the layout.
│ ├─ language/ — administrator language files (loaded in backend).
│ │ ├─ en-GB/ — English (admin).
│ │ │ ├─ en-GB.com_hellonico.ini — strings used inside admin screens.
│ │ │ └─ en-GB.com_hellonico.sys.ini — admin “system” strings (component name/desc in manager, menus, etc.).
│ │ └─ es-ES/ — Spanish (admin).
│ │ ├─ es-ES.com_hellonico.ini — admin internal strings in Spanish.
│ │ └─ es-ES.com_hellonico.sys.ini — admin system strings in Spanish.
│ └─ tmpl/ — admin templates (layouts).
│ └─ hello/
│ └─ default.php — HTML layout for the Hello admin view.
└─ site/ — component frontend (Site application) code.
├─ language/ — site language files (loaded in frontend and by the menu item type selector).
│ ├─ en-GB/ — English (site).
│ │ ├─ en-GB.com_hellonico.ini — strings used inside frontend views.
│ │ └─ en-GB.com_hellonico.sys.ini — **menu item type strings** (title/description shown in selector).
│ └─ es-ES/ — Spanish (site).
│ ├─ es-ES.com_hellonico.ini — frontend internal strings in Spanish.
│ └─ es-ES.com_hellonico.sys.ini — **menu item type strings** in Spanish.
├─ src/ — frontend PHP classes (PSR-4).
│ ├─ Controller/ — site MVC controllers.
│ │ └─ DisplayController.php — default site controller; loads the “hello” view.
│ └─ View/ — site MVC views.
│ └─ Hello/
│ └─ HtmlView.php — site HTML view; reads menu item params and exposes them to the layout.
└─ tmpl/ — frontend templates (layouts).
└─ hello/
├─ default.php — HTML layout rendered on the frontend.
└─ default.xml — **layout metadata**: exposes the **menu item type** and defines its fields.
1.9 Bonus nº1: Ready to download & install
Get the installable ZIP from the repository and upload it in Joomla (System → Extensions → Install): Download ZIP.
1.10 Bonus nº2: Preparing Your Joomla Extension for JED Submission
Once your 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.
