| name | joomla6-extensions |
| description | Core Joomla 6 (6.0-era) extension development skill. Patterns and reference for building components, modules, plugins, templates, libraries, and CLI/console applications using native Joomla 6 MVC, PSR-4 namespacing, dependency injection, SubscriberInterface events, and modern PHP. Excludes Joomla 3/4 J* classes, FOF, and jQuery. |
Joomla 6 Extension Development Skill
Comprehensive reference for building Joomla 6 extensions: components, modules, plugins, templates, libraries, packages, and CLI/console applications — using core Joomla 6 patterns only.
When to Use This Skill
Trigger this skill when:
- Building or updating Joomla 6 components (MVC pattern, admin/site/api)
- Creating or updating Joomla 6 modules (frontend or admin)
- Developing or updating Joomla 6 plugins (event handlers, content filters, system, ajax, console, finder, task, webservices, etc.)
- Building or customizing Joomla 6 templates (frontend or admin child templates)
- Creating library extensions for shared code
- Writing CLI/console applications via console plugins
- Setting up extension installation manifests, install/update scripts, and update servers
- Working with Joomla's Dependency Injection container and
services/provider.php
- Migrating extensions from Joomla 3/4/5 (or FOF) to native Joomla 6
Hard Rules
Every extension built or updated under this skill must:
- Target PHP 8.3/8.4 with
declare(strict_types=1); in every PHP file
- Use PSR-4 namespacing matching the on-disk layout
- Use
\defined('_JEXEC') or die; (with leading backslash inside namespaces)
- Use dependency injection via
ServiceProviderInterface and the DI container
- Use
SubscriberInterface with typed Event objects for all plugin events
- Use
WebAssetManager for all CSS/JS registration
- Use vanilla JavaScript (ES2020+) — never jQuery
- Use Bootstrap 5 classes/utilities — never Bootstrap 2/3/4
Every extension must never:
- Use any
J*-prefixed Joomla 3 class (JFactory, JTable, JModel, JController, JHtml, JText, JRoute, JUri, JRequest, JInput, JLog, JFolder, JFile, JLoader::register, etc.)
- Use FOF code (
F0FController, F0FModel, F0FTable, F0FViewHtml, F0FPlatform)
- Use jQuery in any form (
$(), jQuery, jquery.min.js, $.ajax(), jQuery UI)
- Use
Factory::getDbo() — must use DI / $this->getDatabase() / DatabaseInterface
- Use
Factory::getUser() / Factory::getUser($id) — removed in Joomla 6
- Use
Joomla\CMS\Filesystem\* — moved to Joomla\Filesystem\* in Joomla 6
- Modify any Joomla core file:
/libraries/, /api/, /includes/, /cli/, /installation/, core components (com_content, com_users, com_config, com_modules, com_plugins, com_templates, com_categories, com_menus, com_media, com_installer, com_admin, com_login, com_cpanel), core plugins, core modules, or core templates (cassiopeia, atum)
Extend Joomla via plugins, event subscribers, service overrides, template overrides (in your own template), and your own component/module/plugin/library.
Quick Reference
Component Manifest (Joomla 6)
<?xml version="1.0" encoding="utf-8"?>
<extension type="component" method="upgrade">
<name>com_example</name>
<version>1.0.0</version>
<namespace path="src">Vendor\Component\Example</namespace>
<files folder="site/">
<folder>language</folder>
<folder>src</folder>
<folder>tmpl</folder>
</files>
<administration>
<files folder="admin/">
<folder>forms</folder>
<folder>language</folder>
<folder>services</folder>
<folder>src</folder>
<folder>tmpl</folder>
</files>
</administration>
</extension>
Module Manifest
<?xml version="1.0" encoding="utf-8"?>
<extension type="module" client="site" method="upgrade">
<name>MOD_EXAMPLE</name>
<version>1.0.0</version>
<namespace path="src">Vendor\Module\Example</namespace>
<files>
<folder module="mod_example">services</folder>
<folder>language</folder>
<folder>src</folder>
<folder>tmpl</folder>
</files>
<config>
<fields name="params">
<fieldset name="basic">
<field
name="my_message"
type="text"
label="MOD_EXAMPLE_FIELD_MESSAGE_LABEL"
/>
</fieldset>
</fields>
</config>
</extension>
CRITICAL: The module="mod_example" attribute on the services folder is required in Joomla 5/6. Without it the DI container cannot register the module.
Plugin Manifest
<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="content" method="upgrade">
<name>PLG_CONTENT_EXAMPLE</name>
<version>1.0.0</version>
<namespace path="src">Vendor\Plugin\Content\Example</namespace>
<files>
<folder plugin="example">services</folder>
<folder>src</folder>
<folder>language</folder>
</files>
</extension>
The plugin="example" attribute on the services folder is required.
Component Service Provider (services/provider.php)
<?php
declare(strict_types=1);
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\CMS\Extension\Service\Provider\ComponentDispatcherFactory;
use Joomla\CMS\Extension\Service\Provider\MVCFactory;
use Joomla\CMS\Extension\Service\Provider\RouterFactory;
use Joomla\CMS\HTML\Registry;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Vendor\Component\Example\Administrator\Extension\ExampleComponent;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
return new class () implements ServiceProviderInterface {
public function register(Container $container): void
{
$container->registerServiceProvider(new MVCFactory('\\Vendor\\Component\\Example'));
$container->registerServiceProvider(new ComponentDispatcherFactory('\\Vendor\\Component\\Example'));
$container->registerServiceProvider(new RouterFactory('\\Vendor\\Component\\Example'));
$container->set(
ComponentInterface::class,
function (Container $container) {
$component = new ExampleComponent($container->get(ComponentDispatcherFactoryInterface::class));
$component->setRegistry($container->get(Registry::class));
$component->setMVCFactory($container->get(MVCFactoryInterface::class));
return $component;
}
);
}
};
Module Service Provider
<?php
declare(strict_types=1);
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\Service\Provider\Module;
use Joomla\CMS\Extension\Service\Provider\ModuleDispatcherFactory;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
return new class () implements ServiceProviderInterface {
public function register(Container $container): void
{
$container->registerServiceProvider(new ModuleDispatcherFactory('\\Vendor\\Module\\Example'));
$container->registerServiceProvider(new Module());
}
};
Plugin Service Provider
<?php
declare(strict_types=1);
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Vendor\Plugin\Content\Example\Extension\Example;
return new class () implements ServiceProviderInterface {
public function register(Container $container): void
{
$container->set(
PluginInterface::class,
function (Container $container) {
return new Example(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('content', 'example')
);
}
);
}
};
Plugin Event Handling (Joomla 6 Style)
<?php
declare(strict_types=1);
namespace Vendor\Plugin\Content\Example\Extension;
\defined('_JEXEC') or die;
use Joomla\CMS\Event\Content\ContentPrepareEvent;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
final class Example extends CMSPlugin implements SubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
'onContentPrepare' => 'onContentPrepare',
];
}
public function onContentPrepare(ContentPrepareEvent $event): void
{
$context = $event->getContext();
$item = $event->getItem();
$params = $event->getParams();
$item->text = str_replace('{example}', 'Replaced!', $item->text);
}
}
Console Plugin (CLI Command)
<?php
declare(strict_types=1);
namespace Vendor\Plugin\Console\Example\CliCommand;
\defined('_JEXEC') or die;
use Joomla\Console\Command\AbstractCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
final class RunExampleCommand extends AbstractCommand
{
protected static $defaultName = 'example:run';
protected function configure(): void
{
$this->setDescription('Run the example command');
}
protected function doExecute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->success('Command executed!');
return 0;
}
}
Ajax Plugin Pattern
URL: index.php?option=com_ajax&format=raw&plugin=myfunction
public function onAjaxMyfunction(\Joomla\Event\Event $event): void
{
\Joomla\CMS\Session\Session::checkToken('request')
or jexit(\Joomla\CMS\Language\Text::_('JINVALID_TOKEN'));
$result = ['status' => 'success', 'data' => $data];
$event->setArgument('result', json_encode($result));
}
Update Server XML
<?xml version="1.0" encoding="utf-8"?>
<updates>
<update>
<name>My Extension</name>
<element>com_example</element>
<type>component</type>
<version>1.0.1</version>
<downloadurl type="full">
https://example.com/downloads/com_example_v1.0.1.zip
</downloadurl>
<changelogurl>
https://example.com/updates/changelog.xml
</changelogurl>
</update>
</updates>
Template Color Scheme (Light/Dark)
document.documentElement.dataset.colorScheme = 'dark';
document.dispatchEvent(
new CustomEvent('joomla:color-scheme-change', { bubbles: true })
);
Custom Error Pages (Template)
<?php
\defined('_JEXEC') or die;
$errorCode = $this->error->getCode();
?>
<?php if ($this->countModules('error-' . $errorCode)) : ?>
<jdoc:include type="modules" name="error-<?php echo $errorCode; ?>" />
<?php else : ?>
<h1><?php echo $this->escape($this->error->getMessage()); ?></h1>
<?php endif; ?>
Web Asset Manager (CSS/JS Registration)
$wa = $this->getDocument()->getWebAssetManager();
$wa->getRegistry()->addExtensionRegistryFile('com_example');
$wa->useStyle('com_example.admin')
->useScript('com_example.admin');
joomla.asset.json lives at the extension root (or per-client folder) and declares the assets, dependencies, and attributes.
Common Extension Types
| Type | Location | Purpose |
|---|
| Component | components/com_* and administrator/components/com_* | Main application logic, central page content |
| Module | modules/mod_* (site) or administrator/modules/mod_* (admin) | Position-based content boxes |
| Plugin | plugins/{group}/{name} | Event subscribers, content filters, behaviors |
| Template | templates/{name} (site) or administrator/templates/{name} (admin) | Site/admin appearance and layout |
| Library | libraries/{vendor}/{library} | Shared code across extensions |
| Package | n/a (manifest only) | Bundle multiple extensions in one installer |
Key Namespacing Rules
- The PHP namespace must match the extension manifest's
<namespace path="src"> value.
- All classes live under the
src/ folder.
- The on-disk path under
src/ must mirror the namespace (PSR-4).
- For debugging autoload issues, inspect
administrator/cache/autoload_psr4.php. Delete the file to force regeneration after manual code changes.
- Component namespaces include a client segment:
Administrator, Site, or Api.
- Plugin namespaces follow
{Vendor}\Plugin\{Group}\{Name}\Extension.
- Module namespaces follow
{Vendor}\Module\{Name}\{Administrator|Site}\....
Reference Files
This skill includes detailed documentation in references/:
components.md — MVC components, routing, custom fields
modules.md — Module development, dispatchers, helpers
plugins.md — Plugin events, system/content/ajax/console plugins
templates.md — Color schemes, error pages, RTL support
libraries.md — Library development basics
custom_php.md — CLI scripts, daemon processes, console commands
getting_started.md — Installation, manifests, changelogs
Use view to read specific reference files when detailed information is needed.
Best Practices
- Always use namespaces — required for Joomla 5/6 extensions
- Always use Dependency Injection via
services/provider.php
- Follow native MVC for components — no FOF, no Joomla 3 idioms
- Use Web Asset Manager for all CSS/JS — never raw
<script>/<link> tags
- Implement
SubscriberInterface for plugin events with typed Event objects
- Include an update server for automatic update checks
- Add a changelog for version visibility in the admin
- Use
WebAssetManager extension registry files (joomla.asset.json) over addInlineScript/addInlineStyle whenever possible
- Mark classes
final when they are not designed for extension
- Never modify Joomla core files — extend via plugins, events, services, and overrides
Related Skills
joomla6-general-concepts — DI, ACL, DB, forms, routing, web assets
joomla6-security — XSS, SQLi, CSRF, input handling
joomla6-code-reviewer — audits extension files against these patterns
joomla6-component-architect — component architecture and migration blueprints
Resources