Creating a Plugin for Joomla from Manifest to Events
Learn how Joomla plugin development works from the manifest and service provider to events, installation, security, and practical troubleshooting.
Joomla plugin development lets you add focused behavior to a site without changing Joomla core. A plugin listens for an event, runs its own logic when that event occurs, and can return or modify a result for Joomla or another extension. That makes plugins useful for content processing, authentication, system integrations, privacy controls, and many other targeted jobs.
This tutorial explains the development workflow behind a maintainable Joomla plugin. You will learn how to choose the right extension type, plan an event-driven feature, create the manifest and PHP structure, install and enable the package, and test it safely. If you are new to extension development, it also helps to review how Joomla plugins work before choosing a first project.

What a Joomla plugin does
A plugin is a Joomla extension associated with trigger events. Joomla provides core events, and extensions can also dispatch custom events. When an event fires, Joomla calls the subscribed plugin methods in sequence.
This is different from a component or module. A component usually provides a main application area, while a module displays a block of content in a template position. A plugin works behind the scenes or at a specific point in a request. It can prepare article text, react to user activity, add a result after an article title, or connect Joomla to an external service.
The event model is powerful because it keeps your feature separate from core files. It also gives other extensions a way to respond to actions through custom events. In modern Joomla development, the plugin architecture uses the event dispatcher and subscriber patterns, so your class can declare the events it handles rather than relying on scattered changes throughout the application.
Plan the plugin before writing code
Start with the behavior, not the folder names. Write down four decisions:
- What should the plugin change? Define the input, the expected output, and what should remain untouched.
- Which event matches the job? A content plugin may need an article preparation event, while another feature may belong to a user, system, authentication, or extension-specific event.
- Where should it run? Decide whether the feature belongs on the site client, administrator client, API, console, or more than one application context.
- What settings need to be configurable? Keep credentials, feature switches, allowed contexts, and display choices in plugin parameters rather than hardcoding them.
Choose the narrowest event that meets the requirement. A plugin that runs on every request can add unnecessary work and make debugging harder. A plugin that checks its context and exits early is easier to reason about and safer to maintain.
Keep the boundary clear between a plugin and a larger extension. If the feature needs its own records, administrator screens, complex routing, or a substantial user workflow, a component may be a better foundation. For a practical comparison of extension roles, see this Joomla extension types reference.
Build the Joomla plugin package
A current plugin package normally includes a manifest XML file, a service provider, a namespaced extension class, and language files. A content plugin called shortcodes might use a structure like this:
plg_shortcodes/
├── language/
│ └── en-GB/
│ ├── plg_content_shortcodes.ini
│ └── plg_content_shortcodes.sys.ini
├── services/
│ └── provider.php
├── src/
│ └── Extension/
│ └── Shortcode.php
└── shortcodes.xmlThe exact plugin name and group will change with your feature. The important point is that each name must agree across the package. Mismatches between the manifest, folder, namespace, class, and language filenames are common reasons a package installs but does not behave correctly.
Create the manifest file
The manifest tells Joomla that the package is a plugin, identifies its group, maps its files, declares its namespace, and registers its language files. A simplified structure looks like this:
<?xml version="1.0" encoding="utf-8"?>
<extension method="upgrade" type="plugin" group="content">
<name>PLG_CONTENT_SHORTCODES</name>
<version>1.0</version>
<description>PLG_CONTENT_SHORTCODES_DESCRIPTION</description>
<namespace path="src">Example\Plugin\Content\Shortcodes</namespace>
<files>
<folder plugin="shortcodes">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_content_shortcodes.ini</language>
<language tag="en-GB">language/en-GB/plg_content_shortcodes.sys.ini</language>
</languages>
</extension>The type must be plugin, and group identifies the plugin type, such as content. The plugin attribute on the services folder identifies the entry point element. The manifest filename should match that element, so a plugin element named shortcodes uses shortcodes.xml.
Use language constants for the administrator name and description instead of placing user-facing text directly in the XML. The system language file supplies those values, while the regular language file contains strings used by the plugin at runtime.
Keep namespacing consistent
The namespace in the manifest must match the namespace used by the service provider and the main extension class. The namespace path also needs to point to the directory containing the classes. A common convention is:
Vendor\Plugin\Content\ShortcodesIf the namespace is wrong, Joomla may not locate the class even though the archive appears to install successfully. When diagnosing an autoloading problem, check the generated PSR-4 autoload mapping after reinstalling the extension. Avoid editing generated cache files as a permanent fix. Correct the package and reinstall it instead.
Register the service provider and event subscriber
The service provider connects Joomla's dependency container to the plugin class. It can load configuration and the application object, then create the plugin when Joomla needs it. The official plugin example uses a lazy object, which means the class is instantiated only when one of its subscribed events is triggered.
Your extension class can implement SubscriberInterface and declare its event methods in one place:
namespace Example\Plugin\Content\Shortcodes;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Event\Content\ContentPrepareEvent;
use Joomla\CMS\Event\Content\AfterTitleEvent;
use Joomla\Event\SubscriberInterface;
final class Shortcode extends CMSPlugin implements SubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
'onContentPrepare' => 'replaceShortcodes',
'onContentAfterTitle' => 'addSubtitle',
];
}
public function replaceShortcodes(ContentPrepareEvent $event): void
{
// Validate the client and context before changing content.
}
public function addSubtitle(AfterTitleEvent $event): void
{
// Add a result only when this page matches the feature's purpose.
}
}Use the event object supplied by Joomla rather than relying on global state. For content processing, inspect the context and obtain the article item from the event. If your plugin should only affect articles, return for contacts, feeds, administrator views, or unrelated content contexts.
A content plugin that replaces a token should also define what happens when the token is unknown. Leaving malformed output on a public page is confusing. A translated, harmless message or leaving the original token untouched may be better, depending on the feature. Escape values before inserting them into rendered HTML, and avoid treating article content as trusted input by default.
Add language files and configuration
The system language file normally contains the name and description shown in administrator screens. The regular language file contains messages loaded by the plugin code. Use language constants for every visitor-facing or administrator-facing message so the extension can be translated later.
Plugin parameters belong in the manifest when editors need to control behavior. Examples include:
- An enable or disable switch for an optional feature
- A list of allowed content contexts
- A display position or output format
- An API endpoint or integration mode
- A value that controls how often a task runs
Do not store secrets in language files or expose them in rendered output. Treat configuration values as untrusted input, validate their type and range, and limit permissions for any account used by an integration.
Install and enable the plugin
Once the package contains the manifest, source files, service provider, and language files, compress the plugin directory into a ZIP archive. Install it through Joomla's administrator extension installer, then open the Plugins area and enable it. Installation alone does not activate a plugin.
For a repeatable workflow, develop against a local site instead of changing a production installation directly. A local Joomla development environment makes it easier to inspect logs, reinstall the package, test different settings, and restore a clean database. Keep the ZIP archive and source tree separate so you can tell whether you are testing the packaged files or uncommitted local changes.
After enabling the plugin, create a small test case that triggers the chosen event. If it is a content plugin, publish a test article containing the expected input and display it through the relevant menu item. Test a second content type as well, so you can confirm that the plugin exits when it should.
Test the complete plugin lifecycle
A useful test plan covers more than the happy path. Check the following scenarios:
- The plugin installs without XML or namespace errors.
- The plugin remains inactive until an administrator enables it.
- The intended event calls the expected method.
- An unrelated context produces no unintended change.
- Empty, malformed, and unknown input are handled safely.
- Output is escaped and does not break surrounding markup.
- Language strings appear correctly in the administrator area and site.
- Disabling the plugin removes its behavior without leaving stored content corrupted.
- Reinstalling or upgrading the package preserves valid configuration.
- Error logging identifies failures without exposing passwords, tokens, or private content.
Test with caching enabled and disabled. A correct plugin can appear broken when an old rendered page is being served. When a change does not appear, clear the relevant Joomla cache only after confirming that the code and plugin state are correct. A Joomla caching settings checklist can help separate stale output from an event or packaging problem.
If the plugin appears in the installer but not in the Plugins screen, inspect the manifest type, group, element name, and archive layout. If it appears but never runs, confirm that it is enabled, the event name is correct, the service provider points to the right class, and the request reaches the client where the plugin is allowed to run.
Security practices for Joomla plugin development
A plugin executes inside Joomla's application, so a small mistake can affect every request that reaches its event. Keep the implementation conservative:
- Do not modify Joomla core files.
- Restrict execution by client and event context when the feature does not need global access.
- Validate IDs, paths, URLs, and configuration values before using them.
- Escape output for the context in which it is rendered.
- Use Joomla's permission and authentication APIs instead of inventing access checks.
- Avoid dynamic file inclusion and unvalidated SQL fragments.
- Request the smallest practical set of permissions from external services.
- Log actionable errors without recording credentials or sensitive user data.
- Keep Joomla, PHP, and third-party extensions updated in the development and production environments.
Security maintenance matters after launch as well. If a plugin or another extension is compromised, updating the vulnerable package does not necessarily remove malicious files already placed on the server. Preserve evidence, review unexpected files and logs, rotate affected credentials, and scan the server when an intrusion is suspected.
Common mistakes to avoid
Treating a plugin like a module
A module is built to display output in a template position. A plugin is built to respond to events. If the main requirement is a visible block with parameters and a layout, a module may be the more direct choice. If the requirement is to react when Joomla prepares an article or authenticates a user, a plugin is usually a better fit.
Using the wrong manifest names
The group, plugin element, XML filename, language filenames, and namespace must describe the same extension. Check each name before troubleshooting PHP logic.
Running on every request
Broad event subscriptions can slow a site and create surprising side effects. Filter by client and context as early as possible, and do not perform expensive work when the request does not need the feature.
Testing only one page
A plugin may work on a single article but fail on featured articles, feeds, administrator views, or a different template. Include the contexts that your real site will use, plus an unrelated context that should remain unchanged.
Frequently Asked Questions
What is the difference between a Joomla plugin and an extension?
A plugin is one type of Joomla extension. Components, modules, templates, languages, and libraries are other extension types. Plugins are event-driven, while components usually provide application views and modules usually display blocks in template positions.
Do I need to edit Joomla core files to create a plugin?
No. A plugin should be packaged as a separate extension with its own manifest, source classes, service provider, and language files. Keeping custom code separate makes upgrades and troubleshooting safer.
Why does a Joomla plugin install but not run?
First confirm that it is enabled. Then check the manifest group and element, the services folder mapping, the namespace, the subscribed event name, the client condition, and the context filter. A cache can also hide a correct change, but it should not be the first assumption.
Should a large feature be a plugin?
Use a plugin when the feature responds to events or adds a focused behavior. Consider a component when it needs substantial data management, administrator screens, routing, or a complete workflow. A module is often better for reusable display output.
Conclusion
Successful Joomla plugin development starts with a narrow event-driven requirement and ends with a package that is consistent, testable, secure, and easy to update. Plan the event and execution context first, then align the manifest, namespace, service provider, subscriber class, language files, and configuration. Install the ZIP on a local test site, enable it explicitly, test unrelated contexts, and verify the behavior with caching and security checks in mind.
That workflow gives Joomla Learn readers a reliable foundation for building small integrations today and larger, maintainable extensions later.