Skip to content
Back to skills

Joomla

ASecurity

Joomla 5+ / 6 / 7 extension development for components, modules, plugins, libraries, and templates using modern MVC with PSR-4, DI, and service providers. Trigger on Joomla 5, 5.4, 6, 6.1, 6.2, 7, J5/J6/J7, Joomla CMS, provider.php, manifest XML, install script, scriptfile, or any Joomla extension code. Covers scaffolding; MVC views/models/controllers; service providers; manifests; database migrations; language files; form fields; layout and template overrides; plugin event subscribers (Subsc...

  • 6 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added June 11, 2026
developmentjavascriptgojavaphpc++shellsqlnodetestinggit

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 20 files and shows the line behind each finding

Scanned June 11, 2026

npx -y skills add Joomla-Bible-Study/claude-skill-joomla --skill joomla --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Joomla?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Joomla
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/joomla-bible-study-joomla/badge)](https://www.skillsdirectory.com/skills/joomla-bible-study-joomla)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: joomla
description: Joomla 5+ / 6 / 7 extension development for components, modules, plugins, libraries, and templates using modern MVC with PSR-4, DI, and service providers. Trigger on Joomla 5, 5.4, 6, 6.1, 6.2, 7, J5/J6/J7, Joomla CMS, provider.php, manifest XML, install script, scriptfile, or any Joomla extension code. Covers scaffolding; MVC views/models/controllers; service providers; manifests; database migrations; language files; form fields; layout and template overrides; plugin event subscribers (SubscriberInterface, CMSPlugin); module dispatchers; Web Asset Manager; task / webservices (Joomla JSON:API) / finder / schemaorg plugins; SEF routers. Use for prompts like "add a view", "register web assets", "override a layout" in any Joomla project.
---

# Joomla 5+ Extension Development

This skill guides you through building Joomla 5+ extensions (components, modules, plugins) using modern architecture patterns derived from the [joomla-cms](https://github.com/joomla/joomla-cms) core and real-world production components.

**Target:** Natively Joomla 6, backward compatible with Joomla 5 (no backward compatibility plugin required)
**PHP requirement (Joomla 6.x):** 8.3+ minimum and supported, 8.4 recommended ([source](https://manual.joomla.org/docs/get-started/technical-requirements/))
**Coding standard:** PSR-12 (PHP), Joomla ESLint config (JavaScript)

## Canonical sources

When a Joomla pattern is non-obvious, ambiguous, or might have drifted between versions, verify against upstream before answering. Prefer fetching directly with WebFetch; if the fetch is blocked, fails, or the user is offline, fall back in this order: (1) cite the canonical URL so the user can open it, (2) rely on the patterns documented in this skill and `references/*.md`, (3) ask the user to paste the relevant snippet rather than guess.

**Primary — source of truth for runtime behavior:**

- [`github.com/joomla/joomla-cms`](https://github.com/joomla/joomla-cms) — core CMS. **Active dev branches** (verified 2026-04-30, after J6.1 release):
  - `6.1-dev` — current released J6 line, in patch maintenance. **Default reference for new J6 code.**
  - `6.2-dev` — next J6 minor in development.
  - `5.4-dev` — current released J5 line.
  - `7.0-dev` — next major in development. Where deprecations land for removal (e.g., the `CMSPlugin::__construct(DispatcherInterface, …)` deprecation slated for removal here — see `references/plugin.md`).
  
  Pick the branch matching the target Joomla version for the code you're verifying. Re-check [`/branches`](https://github.com/joomla/joomla-cms/branches) periodically — Joomla cuts new minor branches frequently and the names above will drift.
  - Frontend component examples: [`components/`](https://github.com/joomla/joomla-cms/tree/6.1-dev/components)
  - Backend component examples: [`administrator/components/`](https://github.com/joomla/joomla-cms/tree/6.1-dev/administrator/components)
  - Core plugins: [`plugins/`](https://github.com/joomla/joomla-cms/tree/6.1-dev/plugins)
  - Core modules: [`modules/`](https://github.com/joomla/joomla-cms/tree/6.1-dev/modules) and [`administrator/modules/`](https://github.com/joomla/joomla-cms/tree/6.1-dev/administrator/modules)
  - Framework libraries shipped with the CMS: [`libraries/src/`](https://github.com/joomla/joomla-cms/tree/6.1-dev/libraries/src)
- [`github.com/joomla-framework`](https://github.com/joomla-framework) — standalone Framework packages (DI, Event, Filesystem, etc.) reused by the CMS.

**Documentation:**

- [manual.joomla.org](https://manual.joomla.org/) — current Developer Manual (Joomla 5+). Preferred prose reference.
  - Source repo: [`github.com/joomla/Manual`](https://github.com/joomla/Manual) (default branch `main`) — the Markdown backing the rendered site. Useful when you need to grep, fetch raw, or cite a permalink to a specific page; manual edits / corrections also go here as PRs.
- [api.joomla.org](https://api.joomla.org/) — generated API reference (classes, methods, signatures).
- [framework.joomla.org](https://framework.joomla.org/) — Joomla Framework package docs.
- [docs.joomla.org](https://docs.joomla.org/) — **legacy wiki**. Useful for historical context (J3/J4) but often stale for J5/J6; cross-check against `joomla-cms` HEAD before quoting.

**When citing in generated code or replies:** prefer a permalink to a specific file/line in `joomla-cms` (right-click → "Copy permalink" produces a commit-pinned URL) over a branch link, so the reference does not silently drift.

**Update cadence:** if a WebFetch reveals the upstream pattern differs from what this skill teaches, flag it to the user and recommend opening an issue on `Joomla-Bible-Study/claude-skill-joomla` so the skill can be corrected.

## Coding Standards

**Standards:** PSR-12 for PHP, Joomla ESLint flat config for JavaScript, `joomla/coding-standards` PHPCS ruleset, C++ style (`//`) for inline comments. Every PHP file ships a `@package` / `@copyright` / `@license` header, every class/property/method gets a docblock with `@since`, `@param` blocks align to two-space minimum spacing, and `@return` is always required. For in-flight code where the next release version isn't fixed yet, use the literal `@since __DEPLOY_VERSION__` placeholder — the release build substitutes it with the actual version before the package is archived. JS sources live in `build/media_source/` and compile to `media/<extension>/js/`; the `Joomla` and `bootstrap` globals are pre-registered. For full docblock examples (file header, class, property, method, deprecated, `__DEPLOY_VERSION__` placeholder), JS conventions and JSDoc patterns, the PHPCS install / `phpcs.xml` template, and the inline-comment style rules, read [`references/coding-standards.md`](references/coding-standards.md).

## Quick Start: Which Extension Type?

Before writing code, identify the right extension type:

- **Component** — Full application with admin backend + frontend views, database tables, CRUD operations, menus. Use when you need a complete management interface (e.g., a booking system, directory, inventory tracker, content manager).
- **Module** — Small, self-contained display block (sidebar, footer, header widget). Has a dispatcher, optional helper, and template. No admin CRUD — gets data from components or its own parameters.
- **Plugin** — Event-driven code that hooks into Joomla's lifecycle. Types include: content, system, finder (search), task (scheduled), webservices (API), schemaorg, and more.
- **Library** — Shared PHP code used by multiple extensions. Installed to `libraries/` and autoloaded via PSR-4 namespace. Use when you have utility classes, API wrappers, or shared logic consumed by your components, plugins, and modules.
- **Template** — Controls the site's HTML shell and layout overrides.

For detailed patterns of each type, read the appropriate reference file:
- `references/component.md` — Full component architecture
- `references/module.md` — Module structure and dispatcher pattern
- `references/plugin.md` — Plugin event subscriber pattern
- `references/library.md` — Library structure and packaging

Cross-cutting references (loaded on demand):
- `references/admin-routing.md` — `task=` vs `view=` URL patterns and the checkout pattern that prevents concurrent overwrites
- `references/coding-standards.md` — PSR-12 / PHPDoc / ESLint / PHPCS conventions
- `references/component-advanced.md` — Toolbar API, batch, ordering, tags, versioning, workflow, webservices, mail templates, dashboards, custom rules
- `references/component-lifecycle.md` — Model save flow, `prepareTable()`, `Table::bind()`/`store()`, filter forms, install script, config.xml, site-side differences, HTMLHelper services, `showon` and fieldset tabs
- `references/database.md` — Install SQL / update SQL, `#__` prefix, DDL-vs-DML rule
- `references/editor-api.md` — WYSIWYG editor JS/PHP API + XTD buttons
- `references/form-fields.md` — Built-in field types + custom field authoring
- `references/layouts.md` — `LayoutHelper::render()`, override priority, sublayouts, key built-in layouts
- `references/menu-items.md` — Site-view menu item type XML (request fields, params, useglobal, multi-layout)
- `references/packaging.md` — Manual zip, build scripts, package extensions, include/exclude checklist, **changelog XML**
- `references/testing.md` — PHPUnit + Jest patterns with real Joomla CMS classes
- `references/update-server.md` — Update server XML, `<targetplatform>` regex, SHA hashes, per-type tweaks
- `references/web-assets.md` — `joomla.asset.json` schema, `useStyle`/`useScript`, dependencies
- `references/gotchas.md` — Hard-won J5/J6 pitfalls (controllers, routing, WAM, dark mode, etc.)

Shared cross-extension references (linked from the per-type files above):
- `references/manifest.md` — Universal manifest elements (`<extension>` root, metadata, `<files>`, `<media>`, `<languages>`, `<scriptfile>`, `<update>` / `<updateservers>`)
- `references/install-script.md` — Lifecycle hooks (`preflight`/`install`/`update`/`postflight`/`uninstall`) shared by component/module/plugin/library
- `references/language-files.md` — Filename conventions, key prefixes per type, plurals, `Text::script()` JS registration
- `references/service-provider.md` — `ServiceProviderInterface` pattern + per-type binding table (component/module/plugin)
- `references/component-router.md` — Router class + `RouterServiceInterface` + `RouterFactory` 3-part contract and SEF rules

## Core Architecture Principles

### 1. PSR-4 Namespaces (Everything Lives in src/)

All PHP classes go under `src/` and use PSR-4 autoloading. Joomla maps namespaces declared in the manifest XML.

**Component namespace declaration (manifest XML):**
```xml
<namespace path="src">Vendor\Component\MyComponent</namespace>
```

Joomla auto-expands this to:
- `Vendor\Component\MyComponent\Administrator\` → `admin/src/`
- `Vendor\Component\MyComponent\Site\` → `site/src/`
- `Vendor\Component\MyComponent\Api\` → `admin/src/Api/` (if present)

**Example:**
```xml
<namespace path="src">Acme\Component\Bookings</namespace>
```
- `Acme\Component\Bookings\Administrator\Model\BookingModel` → `admin/src/Model/BookingModel.php`
- `Acme\Component\Bookings\Site\View\Booking\HtmlView` → `site/src/View/Booking/HtmlView.php`

**Module namespace:**
```xml
<namespace path="src">Vendor\Module\MyModule</namespace>
```

**Plugin namespace:**
```xml
<namespace path="src">Vendor\Plugin\PluginGroup\MyPlugin</namespace>
```

Case sensitivity matters on Linux. Directory names and class names must match exactly.

> **Joomla 5/6 Compatibility Note:** `createQuery()` and `\Joomla\Input\Input` work on both Joomla 5 and 6. Writing code this way means it runs natively on J6 without the backward compatibility plugin, while also working fine on J5.

### 2. Service Provider (The Modern Entry Point)

Every extension has `services/provider.php` — this is how Joomla discovers and bootstraps the extension through its DI container. The wrapping pattern (`ServiceProviderInterface` + anonymous class + `register()`) is shared across components, modules, and plugins; for the universal pattern, the per-type binding table, and the common DI pitfalls see [`references/service-provider.md`](references/service-provider.md). The example below is the **component** flavor — it registers `MVCFactory`, `ComponentDispatcherFactory`, `RouterFactory`, and (when applicable) `CategoryFactory`, then binds `ComponentInterface`.

**Component service provider pattern:**
```php
<?php
\defined('_JEXEC') or die;

use Vendor\Component\MyComponent\Administrator\Extension\MyComponentComponent;
use Joomla\CMS\Component\Router\RouterFactoryInterface;
use Joomla\CMS\Dispatcher\ComponentDispatcherFactoryInterface;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\CMS\Extension\Service\Provider\CategoryFactory;
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 Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;

return new class () implements ServiceProviderInterface {
    public function register(Container $container): void
    {
        $container->registerServiceProvider(new CategoryFactory('\\Vendor\\Component\\MyComponent'));
        $container->registerServiceProvider(new MVCFactory('\\Vendor\\Component\\MyComponent'));
        $container->registerServiceProvider(new ComponentDispatcherFactory('\\Vendor\\Component\\MyComponent'));
        $container->registerServiceProvider(new RouterFactory('\\Vendor\\Component\\MyComponent'));

        $container->set(
            ComponentInterface::class,
            function (Container $container) {
                $component = new MyComponentComponent(
                    $container->get(ComponentDispatcherFactoryInterface::class)
                );
                $component->setRegistry($container->get(Registry::class));
                $component->setMVCFactory($container->get(MVCFactoryInterface::class));
                $component->setRouterFactory($container->get(RouterFactoryInterface::class));

                return $component;
            }
        );
    }
};
```

The namespace string passed to each factory (e.g., `'\\Vendor\\Component\\MyComponent'`) tells Joomla where to find your Controller, Model, View, and Table classes. Get this wrong and nothing loads.

### 3. Modern Joomla API — What to Use and What to Avoid

This is critical. Code must work natively on Joomla 6 WITHOUT the "Behaviour - Backward Compatibility 6" plugin, and also run on Joomla 5.

**Use these (Joomla 6 native):**
| Task | Modern API |
|------|-----------|
| Database access | `$this->getDatabase()` or inject `DatabaseInterface` |
| Build queries | `$db->createQuery()` (preferred over `$db->getQuery(true)`) |
| Current user | `$this->getCurrentUser()` in models, `$this->getIdentity()` in views/controllers |
| Input handling | `\Joomla\Input\Input` (NOT `\Joomla\CMS\Input`) — CMS\Input is removed in J6 |
| Create model/table | MVCFactory `$this->getMVCFactory()->createModel('Name')` |
| Input in controllers | `$this->input` (already available, uses `\Joomla\Input\Input`) |
| Error handling | Throw exceptions (`\RuntimeException`, `\InvalidArgumentException`) |
| Web assets | `WebAssetManager` via `$wa = $this->getDocument()->getWebAssetManager()` |
| File operations | PHP native functions or Symfony Filesystem (NOT `\Joomla\CMS\Filesystem`) |
| getItem() return | Treat as `stdClass` (NOT `CMSObject`) — no `->get()` or `->set()` magic |

**Never use these (removed or behind compat plugin in Joomla 6):**
| Deprecated | Why | Replacement |
|-----------|-----|-------------|
| `getDbo()` or `$this->_db` | Removed in Joomla 5 | `$this->getDatabase()` |
| `$db->getQuery(true)` | Still works but deprecated pattern | `$db->createQuery()` |
| `\Joomla\CMS\Input` | Removed in Joomla 6 | `\Joomla\Input\Input` |
| `\Joomla\CMS\Filesystem` | Moved to compat plugin in J6, removed J7 | PHP native / Symfony |
| `CMSObject` properties via `->get()` / `->set()` | `getItem()` returns `stdClass` in J6 | Direct property access: `$item->title` |
| `Factory::getUser()` | Deprecated | `$this->getCurrentUser()` or `$this->getIdentity()` |
| `getSession()->get('user')` | Use identity methods | `$this->getIdentity()` |
| `getError()` / `setError()` on **models** (`BaseDatabaseModel`) | Use exceptions | Throw `\RuntimeException` |
| `new ClassName()` for models | Hard-coded dependencies | `$this->getMVCFactory()->createModel()` |
| `jimport()` | Removed | PSR-4 autoloading |
| `CMSObject` class | Deprecated, removed in J7 | `stdClass` or custom classes |
| `getErrorMsg()` | Removed | Throw exceptions |

### 4. Directory Structure

**Component layout:**
```
com_mycomponent/
├── mycomponent.xml              # Manifest
├── mycomponent.script.php       # Install/update script (optional)
├── admin/
│   ├── services/
│   │   └── provider.php         # DI container registration
│   ├── src/
│   │   ├── Extension/
│   │   │   └── MyComponentComponent.php
│   │   ├── Controller/
│   │   │   ├── DisplayController.php
│   │   │   ├── ItemController.php      # FormController (single item)
│   │   │   └── ItemsController.php     # AdminController (list)
│   │   ├── Model/
│   │   │   ├── ItemModel.php           # FormModel (single)
│   │   │   └── ItemsModel.php          # ListModel (list)
│   │   ├── View/
│   │   │   ├── Item/
│   │   │   │   └── HtmlView.php
│   │   │   └── Items/
│   │   │       └── HtmlView.php
│   │   ├── Table/
│   │   │   └── ItemTable.php
│   │   ├── Field/                      # Custom form fields
│   │   ├── Helper/                     # Utility classes
│   │   ├── Dispatcher/
│   │   │   └── Dispatcher.php
│   │   └── Service/
│   │       └── HTML/                   # HTMLHelper services
│   ├── forms/                          # XML form definitions
│   ├── sql/
│   │   ├── install.mysql.utf8.sql
│   │   └── updates/
│   │       └── mysql/
│   │           └── 1.0.0.sql
│   ├── tmpl/                           # Admin templates
│   │   ├── item/
│   │   │   └── edit.php
│   │   └── items/
│   │       └── default.php
│   ├── language/
│   │   └── en-GB/
│   │       ├── com_mycomponent.ini
│   │       └── com_mycomponent.sys.ini
│   ├── access.xml                      # ACL definitions
│   └── config.xml                      # Component options
├── site/
│   ├── src/
│   │   ├── Controller/
│   │   ├── Model/
│   │   ├── View/
│   │   ├── Dispatcher/
│   │   ├── Helper/
│   │   └── Service/
│   │       └── Router.php              # SEF URL routing
│   ├── forms/
│   ├── tmpl/
│   └── layouts/
└── media/
    ├── joomla.asset.json               # Web asset definitions
    ├── css/
    ├── js/
    └── images/
```

### 5. MVC Request Flow

Understanding the request lifecycle helps you know what to create:

```
URL → Router → Dispatcher → Controller → Model → Table (DB) → View → Template → Response
```

1. **Router** parses the URL to determine component, view, and ID
2. **Dispatcher** (admin/src/Dispatcher/) checks access and creates the controller
3. **Controller** handles the action (display, save, delete, publish)
4. **Model** fetches or manipulates data using database queries
5. **Table** maps to a single database row (load, store, delete, check)
6. **View** (HtmlView.php) prepares data for rendering
7. **Template** (tmpl/) outputs the HTML

### Admin URL Routing (`task=` vs `view=`)

Admin URLs use two patterns: `task={controller}.{method}` triggers a controller action (and checks out the record), while `view={view}` just displays a view. Linking from list views to edit forms MUST use `task=` — using `view=item&layout=edit` skips checkout and lets two users overwrite each other. For the full pattern table (when to use which), the checked-out handling pattern in list templates, and the JOIN that surfaces the editor name, read [`references/admin-routing.md`](references/admin-routing.md).

### 6. Naming Conventions

Joomla has strict naming that connects everything automatically:

| What | Naming Pattern | Example |
|------|---------------|---------|
| Single-item controller | `{Entity}Controller` | `BookingController` |
| List controller | `{Entity}sController` (plural) | `BookingsController` |
| Single-item model | `{Entity}Model` | `BookingModel` |
| List model | `{Entity}sModel` (plural) | `BookingsModel` |
| Table class | `{Entity}Table` | `BookingTable` |
| Single-item view dir | `View/{Entity}/HtmlView.php` | `View/Booking/HtmlView.php` |
| List view dir | `View/{Entity}s/HtmlView.php` | `View/Bookings/HtmlView.php` |
| Edit template | `tmpl/{entity}/edit.php` | `tmpl/booking/edit.php` |
| List template | `tmpl/{entity}s/default.php` | `tmpl/bookings/default.php` |

Some projects use a vendor prefix on entity names (e.g., `AcmeBooking` instead of plain `Booking`). This is optional but helps avoid naming collisions with other extensions.

The view name in the URL (e.g., `&view=bookings`) must match the View directory name (case-insensitive on the URL side, but the directory must match the class namespace).

### 7. Data, distribution, and storage — pointer summary

The data + distribution layers that every extension type touches are documented in their own references so this skill stays focused on the architecture above. Read the one(s) you need:

- **Database schema** — install SQL (`admin/sql/install.mysql.utf8.sql`), update SQL files in `admin/sql/updates/mysql/`, the `#__` prefix Joomla replaces at runtime, and the DDL-vs-DML rule (SQL files do DDL only; data migrations go in the install script). [`references/database.md`](references/database.md).
- **Language files** — INI file conventions, key prefixes per extension type (`COM_`, `MOD_`, `PLG_<GROUP>_<ELEMENT>_`, `LIB_`), plurals, and `Text::script()` JS registration. [`references/language-files.md`](references/language-files.md).
- **Web Asset Manager** — `joomla.asset.json` schema, `$wa->useStyle()` / `$wa->useScript()`, dependencies, attributes. [`references/web-assets.md`](references/web-assets.md).
- **Manifest XML** — `<extension>` root attributes, `<files>`, `<media>`, `<languages>`, `<scriptfile>`, the `<administration>` block with `<menu>` / `<submenu>`, and the `<install>` / `<update>` blocks. Universal elements in [`references/manifest.md`](references/manifest.md); the full component-flavored manifest template in [`references/component.md`](references/component.md).
- **Install / update scripts** — `preflight` / `install` / `update` / `postflight` / `uninstall` hooks and DML migrations. [`references/install-script.md`](references/install-script.md).
- **Changelog XML** — what powers the in-admin "View changelog" link; categories (`<security>`, `<fix>`, etc.), required nodes, element/type values per extension type. [`references/packaging.md`](references/packaging.md) § Changelog XML.
- **Update server** — update XML, `<targetplatform>` regex, SHA hashes, per-extension-type tweaks (`folder` / `client` attributes). [`references/update-server.md`](references/update-server.md).

## Workflow: Adding a New Entity to a Component

When adding a new entity (e.g., "Location" to a component), create these files:

1. **Table** — `admin/src/Table/LocationTable.php`
2. **Model (single)** — `admin/src/Model/LocationModel.php` (extends FormModel)
3. **Model (list)** — `admin/src/Model/LocationsModel.php` (extends ListModel)
4. **Controller (single)** — `admin/src/Controller/LocationController.php` (extends FormController)
5. **Controller (list)** — `admin/src/Controller/LocationsController.php` (extends AdminController)
6. **View (edit)** — `admin/src/View/Location/HtmlView.php`
7. **View (list)** — `admin/src/View/Locations/HtmlView.php`
8. **Templates** — `admin/tmpl/location/edit.php` and `admin/tmpl/locations/default.php`
9. **Form XML** — `admin/forms/location.xml`
10. **SQL** — Add CREATE TABLE to install SQL, add update SQL file
11. **Language strings** — Add to `.ini` files
12. **Menu entry** — Add `<menu>` to manifest XML submenu

For detailed code templates of each file, read `references/component.md`.

## Component Lifecycle & Patterns

The internal mechanics of how components work — the model save flow (`Controller::save()` → `Model::prepareTable()` → `Table::bind()` → `Table::check()` → `Table::store()`), the `getItem()` / `save()` model overrides, `Table::bind()` JSON-encoding params, filter form XML for searchtools, the component install/update script class, `config.xml` component options, site-side differences (caching, access filtering, menu params, page metadata), the Extension class implementing `CategoryServiceInterface` / `TagServiceInterface`, custom controller tasks and AJAX, registering HTMLHelper services, and form XML conditional fields (`showon`) plus fieldset-to-tab rendering — all live in [`references/component-lifecycle.md`](references/component-lifecycle.md).

## Advanced Component Features

Component-level features beyond core MVC: the toolbar API (`getDocument()->getToolbar()`, dropdown groups, save dropdowns), batch processing (`AdminModel` batch methods, dialog template, controller `batch()` override), drag-drop ordering (the `ordering` column, `js-draggable` markup, AJAX save), tags integration (`TagServiceInterface`, `TaggableTableInterface`), content versioning (`VersionableModelInterface`, `#__content_types` registration, `save_history` config), workflow integration (`WorkflowServiceInterface`), the webservices API plugin (`onBeforeApiRoute`, `createCRUDRoutes`, JSON:API view), mail templates (`MailTemplate`, `#__mail_templates`), dashboard views (manifest `<dashboards>`, presets, `cpanel-*` positions), and custom form validation rules (`FormRule`, `addruleprefix`). For full code examples and the toolbar method list, read [`references/component-advanced.md`](references/component-advanced.md).

## Editor API

Joomla's Editor API provides a unified interface for WYSIWYG editors (TinyMCE, CodeMirror, None) and editor extension buttons. Coverage: the modern `JoomlaEditor` JS API (`get`, `getActive`, `getValue`, `setValue`, `getSelection`, `replaceSelection`, `disable`, `getRawInstance`, `getType`); the legacy `Joomla.editors.instances` proxy; the `JoomlaEditorDecorator` pattern for implementing a custom editor; XTD button plugins (`onEditorButtonsSetup`, `Button` class, `JoomlaEditorButton.registerAction`); the modal-button content-selection flow with `postMessage` and `joomla:content-select`; the `editor` form field type with all attributes (`buttons`, `hide`, `height`, `width`, `editor`, `filter`, `asset_field`, `created_by_field`, `syntax`); and editor plugin registration via `onEditorSetup` with `AbstractEditorProvider`.

For full code examples and the button-action behavior table, read [`references/editor-api.md`](references/editor-api.md).

## Layouts (LayoutHelper)

Joomla's layout system provides reusable, overridable PHP template fragments via `LayoutHelper::render()`. Layout IDs are dot-separated paths (`joomla.form.field.text` → `layouts/joomla/form/field/text.php`). Templates can override any layout by copying it to `templates/{template}/html/layouts/`. For the rendering API, layout-override priority table, sublayout pattern, and the list of key built-in layouts (searchtools, edit.title_alias, batch.*, content.info_block, form.renderfield, pagination.default, etc.), read [`references/layouts.md`](references/layouts.md).


## Form Fields

Joomla ships ~90 built-in form field types and a clean extension model for custom field classes. Coverage: built-in field reference (basic inputs, selection fields, file/media fields, special fields including `subform`, `rules`, `ordering`, `note`, `componentlayout`), the `subform` repeatable-group pattern with external/inline form sources, the `media` picker (`types`, `preview`, `directory` attributes), authoring custom fields by extending `ListField` / `GroupedlistField` / `FormField` / `TextField` / `PredefinedlistField`, the `addfieldprefix` form attribute, and modern layout-based field rendering with `getLayoutData()`.

For the full type reference, custom-field examples, and layout overrides, read [`references/form-fields.md`](references/form-fields.md).

## Menu Item Types (Site Views)

Menu item types define what appears in Joomla's Menu Manager. Joomla scans `components/{component}/tmpl/{view}/` for XML files: each `.xml` matching a layout name (`default.xml`, `blog.xml`, etc.) becomes a selectable menu item type. For the full XML structure (list and single-item variants), `<layout>` / `<fields name="request">` / `<fields name="params">` semantics, the `useglobal="true"` cascade rule, and the multi-layout-per-view convention, read [`references/menu-items.md`](references/menu-items.md).

## Libraries and Composer

Libraries are shared PHP packages installed under `libraries/` that any extension can consume; Composer is the package manager for any third-party PHP libs you bundle into your extension. The full reference — when to use a library vs. a helper, the `composer.json` template, the `vendor-dir: libraries/vendor` convention, runtime autoload-loading from `provider.php`, the common Joomla-ecosystem libraries to use vs. avoid duplicating, and npm front-end build tooling — lives in [`references/library.md`](references/library.md).

## Extension Packaging (Building the Installable ZIP)

Joomla extensions ship as ZIP files installed through the admin installer (`System → Install → Extensions`). For manual packaging, the build-script pattern, package manifests (multiple extensions bundled), and the include/exclude checklist for what belongs in the ZIP, read [`references/packaging.md`](references/packaging.md).

## Real-World Reference: Production Components

When working on a Joomla project, check whether the project has its own `CLAUDE.md` or `CONTRIBUTING.md` at the repo root — these often define project-specific naming conventions, build commands, and coding standards that should take precedence over this skill's generic patterns.

Common patterns seen in production Joomla 5+ components:

- **Entity prefixes** — Some projects prefix entity names to avoid collisions (e.g., a `<Vendor>` prefix on every Model/View/Table class). Follow the project's existing convention.
- **Plugin groups** — Real components typically ship with several plugin types: content, finder (Smart Search), schemaorg (structured data), system, task (scheduled jobs), and webservices (REST API).
- **Module variants** — Both admin-side and site-side modules in `modules/admin/` and `modules/site/`.
- **Build tooling** — Composer for PHP dependencies + npm for JS/CSS asset compilation. Look for `composer.json` and `package.json` at the project root.
- **Testing** — PHPUnit for PHP (unit + integration tests in `tests/`), Jest for JavaScript. See the **Testing** section below for patterns.
- **Non-standard vendor paths** — Some projects place Composer's `vendor/` directory in a custom location (e.g., `libraries/vendor/`). Check `composer.json` for the `vendor-dir` config.
- **Media asset pipeline** — Source JS/CSS in a build directory (e.g., `build/media_source/`) compiled to `media/` via npm scripts. The `media/` directory may be gitignored.

## Testing

PHPUnit (PHP) and Jest (JavaScript) patterns for Joomla 5+ extensions, anchored on the principle Joomla core uses for its own tests: **load real CMS classes, don't stub them.** Topics covered: `tests/Unit/` + `tests/Integration/` directory layout, `phpunit.xml` configuration, the bootstrap that loads Joomla's vendor autoloader plus your component's PSR-4 autoloader, the `getQueryStub()` helper for a real `DatabaseQuery` with only 2 abstract methods stubbed, model/table/helper test patterns, Jest with jsdom for JavaScript with mocked `Joomla` globals, and four high-stakes testing gotchas (`DatabaseInterface` lacks `createQuery()`, `CMSApplicationInterface` lacks `getSession()`, the bootstrap-must-load-`libraries/vendor/autoload.php` rule, `createMock()` vs `createStub()` for expectations).

For full setup, code patterns, and gotcha details, read [`references/testing.md`](references/testing.md).

## Version Migration: Joomla 5 → 6 (and Beyond)

When migrating an extension from Joomla 5 to Joomla 6 (or writing code that supports both), apply these changes systematically:

### Step 1: Query Builder
Find all instances of `$db->getQuery(true)` and replace with `$db->createQuery()`. Both work on Joomla 5, but only `createQuery()` is guaranteed going forward.

### Step 2: Input Classes
Replace any `use Joomla\CMS\Input\Input` with `use Joomla\Input\Input`. The CMS wrapper is removed in Joomla 6. Note: `$this->input` in controllers already works correctly on both versions.

### Step 3: CMSObject → stdClass
If code uses `$item->get('property')` or `$item->set('property', $value)` on objects returned by `getItem()`, replace with direct property access: `$item->property` and `$item->property = $value`. In Joomla 6, `getItem()` returns `stdClass`, not `CMSObject`.

### Step 4: Filesystem Classes
Replace `use Joomla\CMS\Filesystem\File` / `Folder` / `Path` with PHP native functions (`file_put_contents`, `mkdir`, `is_dir`, etc.) or Symfony Filesystem. The CMS Filesystem classes move behind the compat plugin in J6 and are removed in J7.

### Step 5: Factory Methods
Search for and replace:
- `Factory::getUser()` → `$this->getCurrentUser()` (in models) or `$this->getIdentity()` (in controllers/views)
- `Factory::getApplication()` → Inject via constructor or use `$this->getApplication()`
- `Factory::getDbo()` → `$this->getDatabase()` or inject `DatabaseInterface`

### Step 6: Error Handling
Replace `$model->getError()` / `$model->setError()` patterns with try/catch and thrown exceptions (`\RuntimeException`, `\InvalidArgumentException`).

### Step 7: Test
After migration, test with the "Behaviour - Backward Compatibility 6" plugin **disabled** to confirm the extension runs natively on Joomla 6 without compatibility shims.

### Future Versions
This skill targets Joomla 6 native patterns that also work on Joomla 5. As Joomla 7+ introduces additional changes, this migration checklist will expand. The core principle remains: use the modern API from the framework layer (`Joomla\Database`, `Joomla\Input`) rather than the CMS convenience wrappers.

## Common Gotchas & Pitfalls

Hard-won lessons from real Joomla 5/6 extension development — easy to get wrong because IDE autocompletion, documentation gaps, or reasonable assumptions lead you astray. Topics covered: `BaseController` vs `FormController`, J5 controller API differences (`$this->input` not `getInput()`), event dispatching for J5 compatibility, plugin manifest naming, plugin language file conventions, task plugin language keys, `AdminModel`/`Table` save workflow, `task=` routing, `form.validate` asset, `Table::check()`, HTTP client class, `Registry::get()` defaults, `Text::script()` registration, `Joomla.Text._()` truthy-key trap, batch routing, **the 3-part SEF router contract** (router class + `RouterServiceInterface` + `RouterFactory`), hidden menu items for SEF, router callback naming, **WAM URI auto-resolution / non-standard paths / inline assets**, Bootstrap 5.3 dark mode classes, dynamic modal cleanup, and `getStoreId()` in `ListModel`.

For full details and code examples, read [`references/gotchas.md`](references/gotchas.md).

Files in this skill

  • SKILL.md34.5 KB
  • references/admin-routing.md3.1 KB
  • references/coding-standards.md9.4 KB
  • references/component-advanced.md17 KB
  • references/component-lifecycle.md17.4 KB
  • references/component-router.md10.8 KB
  • references/component.md47.5 KB
  • references/database.md3 KB
  • references/editor-api.md8.1 KB
  • references/form-fields.md8.2 KB
  • references/gotchas.md19 KB
  • references/install-script.md9.1 KB
  • references/language-files.md7.4 KB
  • references/layouts.md3.3 KB
  • references/library.md15.5 KB
  • references/manifest.md9 KB
  • references/menu-items.md5.3 KB
  • references/module.md14.9 KB
  • references/packaging.md6.1 KB
  • references/plugin.md19.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.