
Extensions in Joomla
A Joomla site is not one big program. It is the Joomla core plus a stack of extensions. An extension is the umbrella word for everything you can add to Joomla: components, modules, plugins, templates, languages, and the packaging that bundles them together.
This is the idea that connects the five extension types you may already know. They are not five unrelated things. They are all kinds of extension, and they share the same rules: a manifest file that describes them, a row in one database table that records them, and one installer that puts them in place and keeps them updated.
Components, modules, plugins, templates, languages and the machinery underneath: the manifest, the installer, and the one table that records them all.
This article explains how extensions really work. It covers the basics for website owners, the practical management screens for administrators, and the technical details for developers. You will learn what an extension is, how Joomla stores it, what the manifest does, how installing and updating work, and how to keep your extensions secure.
The goal is simple: help you understand Joomla extensions well enough to install, update, and troubleshoot them with confidence.
1. The Basics
1.1 What "Extension" Actually Means
Almost everything in the Joomla backend is an extension, and so is most of what you install from elsewhere. A simple test works well:
If you can install it, enable it, or uninstall it, then it is an extension.
Joomla is the platform. Extensions are everything you bolt onto it. The core CMS provides the framework, the application, and the shared libraries. Every functional thing on a page is delivered through an extension that the installer manages.
+--------------------------------------+
| JOOMLA CORE (CMS) |
| framework . application . libraries|
+--------------------------------------+
^
installed & managed | through one pipeline
|
+----------+----------+----+-----+-----------+-----------+
| Component| Module | Plugin | Template | Language | ...packages
| com_ | mod_ | plg_ | tpl_ | xx-XX | pkg_, lib_, file
+----------+----------+----------+-----------+-----------+
1.2 The Five Functional Types
These five are the extension types that do something visible on a page. You have probably met each of them already:
| Type | Role | Per page | Prefix |
|---|---|---|---|
| Component | Main page content or application | exactly 1 | com_ |
| Module | Small boxes around the content | many | mod_ |
| Plugin | Event-driven behaviour in the background | many | plg_ |
| Template | Look, feel, and page layout | 1 site + 1 admin | tpl_ |
| Language | Translations | 1 active | xx-XX |
But the extension family is bigger than five. The rest are about packaging and support.
1.3 The Full Taxonomy
Joomla's installer recognises more type values than just the visible five. The extra ones are the "glue" that helps you ship and reuse code:
| Type | Prefix | What it is |
|---|---|---|
component |
com_ |
A mini-application, the engine of the page |
module |
mod_ |
A reusable box on the page |
plugin |
plg_ |
An event listener |
template |
tpl_ |
Page look and layout |
language |
xx-XX |
A translation pack |
package |
pkg_ |
A bundle of several extensions installed together |
library |
lib_ |
Shared PHP code reused by other extensions |
file |
- | Loose files dropped into specific folders (for example the core "Joomla! Files") |
A real-world product, such as a forum or a webshop, usually ships as one pkg_ package that contains a component, several plugins, and a few modules.
1.4 You Already Run Dozens of Extensions
A brand-new Joomla 6 install ships with around 150 extensions out of the box. Open System → Manage → Extensions and you will see them all: components like com_content, every core plugin, both default templates, the English language pack, and the libraries underneath.
There is no "core versus extensions" wall. Core Joomla is delivered as extensions. They are simply marked protected, so you cannot remove them by accident.
On top of the core set, the Joomla Extensions Directory (JED) lists thousands of third-party extensions you can add. Well-known examples include Akeeba Backup for backups, the Regular Labs content tools, and JCE for a richer editor.
Back to top2. The #__extensions Table
2.1 One Table Records Every Extension
Whatever the type, every installed extension is exactly one row in the #__extensions table. This single table is the registry of your whole site. The #__ is Joomla's table prefix placeholder; on a real site it might be jos_extensions.
| Column | Meaning |
|---|---|
extension_id |
Primary key |
name |
Internal name (com_content, plg_content_joomla) |
type |
component / module / plugin / template / language / package / library / file |
element |
The element name (com_content, joomla, protostar) |
folder |
Plugins only - the plugin group (content, system...) |
client_id |
0 = site, 1 = administrator |
enabled |
1 / 0 |
access |
View access level |
protected |
1 = cannot be uninstalled (core) |
locked |
1 = cannot be disabled or edited |
manifest_cache |
JSON snapshot of the manifest (version, author...) |
params |
JSON - the extension's saved Options |
2.2 What Makes an Extension Unique
No single column identifies an extension. It is a combination of four columns:
type + element + folder + client_id = one unique extension
A few real examples show why all four are needed:
component | com_content | (none) | 1 → the Articles admin app
module | mod_menu | (none) | 0 → the site menu module
module | mod_menu | (none) | 1 → the admin menu module (same element, different client)
plugin | joomla | content | 0 → plg_content_joomla
plugin | joomla | user | 0 → plg_user_joomla (same element, different folder)
This is why plugins carry a folder (their group) and why the same module element can exist twice: once for the site, once for the administrator.
2.3 manifest_cache and params
Two JSON columns do a lot of work:
manifest_cacheis a cached copy of the manifest's metadata: version, author, creation date, and description. The Manage list reads this, so it never has to re-parse the XML on every page load.paramsholds the extension's Options. The "Options" button on any component, the settings on a plugin, the parameters of a module - all of them are serialised here as JSON.
When you click Options on any extension, you are editing the params JSON in this row. There is no separate config table.
3. The Manifest: The Heart of Every Extension
3.1 The Self-describing XML File
Every extension contains one manifest: an XML file that tells Joomla everything about it. The installer reads nothing else first. Hand Joomla a zip with a manifest, and it knows how to install the extension, where its files go, what database tables to create, and how to update it later.
<extension type="component" method="upgrade">
<name>com_example</name>
<version>1.0.0</version>
<creationDate>2026-06-01</creationDate>
<author>Peter Martin</author>
<namespace path="src">Joomla\Component\Example</namespace>
<files folder="site">...</files> <!-- which files to copy -->
<media destination="com_example">...</media> <!-- images, css, js -->
<languages>...</languages> <!-- translation files -->
<install><sql>...</sql></install> <!-- run on install -->
<update><schemas>...</schemas></update> <!-- run on update -->
<uninstall><sql>...</sql></uninstall> <!-- run on remove -->
<scriptfile>script.php</scriptfile> <!-- custom install logic -->
<updateservers>...</updateservers> <!-- where updates live -->
<config>...</config> <!-- the Options form -->
</extension>
One file drives install, update, uninstall, configuration, and updates. Change the type attribute and the same structure describes a module, plugin, template, or package.
3.2 The Attributes That Change Everything
A few attributes on the root <extension> element decide how the installer behaves:
| Attribute | Values | Effect |
|---|---|---|
type |
component, module, plugin, template, language, package, library, file |
What kind of extension this is - decides where files go |
method |
install / upgrade |
upgrade overwrites existing files if already installed (the modern default) |
group |
for example content, system |
Plugins only - which group or folder to install into |
client |
site / administrator |
Modules and templates - which application it belongs to |
method="upgrade" is why re-installing the same zip updates in place instead of failing with an "already installed" error.
3.3 The Install Script: Custom Logic
For anything the manifest's declarative tags cannot express, an extension can ship a script file with up to five methods:
class com_exampleInstallerScript
{
public function preflight($type, $parent) { /* before files are copied */ }
public function install($parent) { /* fresh install only */ }
public function update($parent) { /* update only */ }
public function uninstall($parent) { /* on removal */ }
public function postflight($type, $parent) { /* after files are copied */ }
}
| Method | Runs |
|---|---|
preflight |
Before copying - version checks, abort if requirements are not met |
install / update / uninstall |
The matching action only |
postflight |
After copying - seed data, show a welcome message, clean up |
Use preflight to block installation on an unsupported PHP or Joomla version by returning false.
3.4 Installer Events: Plugins That Hook Install and Update
The script file affects only its own extension. But the installer also fires events that any enabled plugin can listen to. This lets a plugin react when something else is installed, updated, or removed.
| Event | Fires when |
|---|---|
onExtensionBeforeInstall / onExtensionAfterInstall |
Around installing any extension |
onExtensionBeforeUpdate / onExtensionAfterUpdate |
Around updating any extension |
onExtensionBeforeUninstall / onExtensionAfterUninstall |
Around removing any extension |
onInstallerBeforePackageDownload |
Just before a package is downloaded (for example to inject a download key) |
// A commercial vendor's plugin injects its subscription key into the download URL:
public function onInstallerBeforePackageDownload(&$url, &$headers)
{
if (str_contains($url, 'example.org')) {
$url .= '&key=' . $this->params->get('download_key');
}
}
This is exactly how paid extensions add a download or subscription key at update time: an ordinary installer plugin listening to these events, with no change to com_installer.
4. Installing Extensions
4.1 com_installer: The Install and Update Component
Everything in this article is managed by one core component, com_installer, surfaced under the System menu:
System
+-- Install → Extensions (add new)
+-- Manage → Extensions (enable / disable / uninstall, see all rows)
+-- Update → Extensions (pending updates)
+-- Manage → Database (schema check & fix)
+-- Manage → Discover (find files already on disk)
+-- Manage → Warnings (environment issues)
4.2 The Four Ways to Install
| Method | How | When |
|---|---|---|
| Upload Package File | Drag-and-drop or pick a .zip |
The normal way - you have the file |
| Install from Folder | Give a server path to an unzipped folder | Large extensions, or files uploaded by FTP |
| Install from URL | Paste a direct link to a .zip |
The vendor gives a download URL |
| Install from Web | Browse the JED inside Joomla and install with one click | Discoverability - search the directory in place |
"Install from Web" talks to the Joomla Extensions Directory through the Install from Web plugin. It is the app-store experience built into the backend.
4.3 What "Install" Actually Does
Handing Joomla a zip starts a fixed sequence:
1. Unzip to a temp folder
2. Find & parse the manifest → read type, files, sql, scriptfile
3. preflight() → may abort
4. Copy files to their homes → components/, modules/, plugins/, media/
5. Run install SQL → CREATE TABLE #__example...
6. Register the PSR-4 namespace
7. Write the #__extensions row + manifest_cache
8. Register update sites
9. postflight() → seed data, messages
10. Clean up the temp folder
If any step fails, Joomla rolls back. No half-installed extension is left behind.
4.4 Discover: Installing What Is Already on Disk
Sometimes files arrive without going through the installer: an FTP upload, a git checkout, or a copied folder. They exist on disk but have no #__extensions row, so Joomla ignores them.
Manage → Discover → scans the standard folders for un-registered manifests
→ lists them → you click Install → rows are created
Discover is the bridge between "files on disk" and "an extension Joomla actually knows about". No file copying happens; it only creates the missing database rows.
4.5 Database: Keeping the Schema in Sync
The Manage → Database screen compares the SQL update files in each extension against what is actually in your database.
- It detects tables or columns that should exist after an update but do not.
- A Fix button replays the missing schema changes.
- This is common after a manual file update where the SQL never ran, or after an interrupted update.
A red "Database schema is out of date" warning almost always means: go here and click Fix.
4.6 #__schemas: The Schema Version Log
How does the Database screen know a table is out of date? It compares each extension's SQL update files against a recorded version in #__schemas.
#__schemas
+-- extension_id | version_id
+-- 27 (com_content) | 6.0.0
+-- 10000 (com_example) | 1.1.0
- Each extension's update folder holds SQL files named after versions, for example
sql/updates/mysql/1.1.0.sql. - After running them, Joomla writes the highest applied version into
#__schemas. - On the next update it runs only the SQL files newer than the stored version. This is the migration "high-water mark".
So #__extensions.manifest_cache stores the file version, while #__schemas stores the database version. When the two disagree, that is the "schema out of date" warning, and the Fix button replays the missing SQL.
5. The Update System
5.1 Update Sites: How Joomla Knows an Update Exists
Each extension can register one or more update sites: URLs that Joomla polls to ask "is there a newer version?". They live in #__update_sites, linked to extensions through #__update_sites_extensions.
#__extensions --< #__update_sites_extensions >-- #__update_sites
(com_example) (https://.../update.xml)
The update site is declared in the manifest:
<updateservers>
<server type="extension" name="Example Updates">
https://example.org/updates/com_example.xml
</server>
</updateservers>
No update site means Joomla can never offer an update for that extension. This is why some manually-installed extensions never show an update notice.
5.2 The Update Server XML
The URL points to an XML file the vendor hosts. There are two flavours:
| Type | Describes | Use |
|---|---|---|
extension |
Available versions of a single extension | One product |
collection |
A list of extension update manifests | A vendor with multiple products |
Extension update manifest example:
<updates>
<update>
<name>com_example</name>
<version>1.1.0</version>
<infourl>https://example.org/changelog</infourl>
<downloads>
<downloadurl type="full" format="zip">
https://example.org/dl/com_example-1.1.0.zip
</downloadurl>
</downloads>
<targetplatform name="joomla" version="6.[01]" />
<php_minimum>8.1</php_minimum>
</update>
</updates>
Collection update manifest example
A collection manifest contains references to one or more extension update manifests. Each extension listed in the collection has its own extension update manifest.
<?xml version="1.0" encoding="utf-8"?>
<extensionset name="Example Extensions">
<extension>
<name>com_example</name>
<updateservers>
<server type="extension" priority="1" name="com_example">
https://example.org/updates/com_example.xml
</server>
</updateservers>
</extension>
<extension>
<name>com_another_example</name>
<updateservers>
<server type="extension" priority="1" name="com_another_example">
https://example.org/updates/com_another_example.xml
</server>
</updateservers>
</extension>
</extensionset>
The targetplatform and php_minimum elements allow a vendor to provide different update packages for specific Joomla or PHP versions from the same update site.
5.3 The Update Flow
Cron / page load / "Check for updates"
|
v
Joomla fetches each update site's XML
|
v
Compares the advertised <version> with the installed version
|
v
Newer? → row in #__updates → shown under Update → Extensions
|
v
You click Update → Joomla downloads the <downloadurl> zip
|
v
...and runs the exact same install pipeline (method="upgrade")
An update is simply an install of a newer zip over the top: the same manifest, the same SQL-update step, the same rollback safety.
5.4 Joomla! Update versus Extension Updates
These are two separate things, and people often confuse them:
| Joomla! Update | Extension updates | |
|---|---|---|
| Component | com_joomlaupdate |
com_installer |
| Updates | The CMS core itself | Your installed add-ons |
| Source | Joomla's official update server | Each extension's own update site |
| Where | System → Update → Joomla | System → Update → Extensions |
Keep both current. A modern, patched core with an outdated, vulnerable extension is still a vulnerable site.
Back to top6. Packages: Bundling Extensions
6.1 Why Packages Exist
Most real products are more than one extension. A webshop might need a component (the store), plugins (payment, search), and modules (cart, featured products). Shipping eight separate zips is painful, so they are wrapped in one pkg_ package.
pkg_shop.zip
+-- pkg_shop.xml <- the package manifest
+-- com_shop.zip <- child: the component
+-- plg_system_shop.zip <- child: a plugin
+-- plg_search_shop.zip <- child: a plugin
+-- mod_shop_cart.zip <- child: a module
+-- mod_shop_featured.zip <- child: a module
6.2 The Package Manifest
A package manifest mostly just lists its children and the order in which to install them:
<extension type="package" method="upgrade">
<name>Shop Package</name>
<packagename>shop</packagename>
<version>2.0.0</version>
<files>
<file type="component" id="com_shop">com_shop.zip</file>
<file type="plugin" group="system" id="shop">plg_system_shop.zip</file>
<file type="module" client="site" id="mod_shop_cart">mod_shop_cart.zip</file>
</files>
</extension>
| Feature | Effect |
|---|---|
| One install | Installs every child in the listed order |
| One uninstall | Removes all children together |
| One update site | Update the whole bundle at once |
| Own #__extensions row | type=package - the parent record |
Uninstalling the package removes its children too. A child marked with <block> in the manifest can be protected from individual removal.
7. Managing and Securing Extensions
7.1 The Manage Screen: Your Control Panel
System → Manage → Extensions lists every row in #__extensions:
| Action | Effect |
|---|---|
| Enable / Disable | Toggles enabled - disabled extensions do not run |
| Uninstall | Removes files, runs uninstall SQL, deletes the row |
| Filter | By type, by location (site or admin), by status |
| Protected badge | Core - cannot be uninstalled |
| Locked badge | Cannot be disabled or edited |
protected and locked are why you cannot cripple your site by accident, for example by disabling com_content or removing the default language.
7.2 The Warnings Tab
Manage → Warnings surfaces environment problems the installer detected: missing PHP extensions, unwritable folders, or leftover files from a failed install. Check it when an install "succeeded" but the extension misbehaves.
7.3 Security: The Part That Bites People
Extensions run with full access to your site. A malicious or abandoned extension is the most common way Joomla sites get compromised.
| Practice | Why |
|---|---|
| Install only from trusted sources | Prefer the JED; avoid "nulled" or pirated copies, which often contain malware |
| Keep everything updated | Most hacks exploit a known, patched vulnerability in an outdated extension |
| Watch the JED VEL | The Vulnerable Extensions List publishes known-bad extensions |
| Remove what you do not use | Disabled is not enough - uninstall dead extensions; less code means less attack surface |
| Check it is maintained | An extension with no update in years is a liability |
| Least privilege | Do not grant extensions or their users more ACL than they need |
The Joomla CMS core has a strong security track record. The weak link is almost always a third-party extension that was not updated.
7.4 Picking the Right Type to Build
When you need to extend Joomla yourself, choose the type by the job:
| You want to... | Build a... |
|---|---|
| Produce the main page content or a new app | Component (com_) |
| Show a reusable box around the content | Module (mod_) |
| React to an event or change behaviour | Plugin (plg_) |
| Change the look and page layout | Template (tpl_) - or a template override |
| Translate strings | Language pack |
| Ship several of the above together | Package (pkg_) |
| Share code between your own extensions | Library (lib_) |
The honest first question is often "do I even need a new extension?". A template override, a custom field, or a language override solves many needs with zero code to maintain.
Back to top8. Extensions and the Shared Services
8.1 Extensions Do Not Reinvent: They Opt In
The deepest lesson across this whole series is that extensions are powerful precisely because they share Joomla's central services instead of duplicating them.
| Shared service | Provided by | Any extension can... |
|---|---|---|
| Categories | com_categories |
Organise its items in one shared hierarchy |
| Custom Fields | com_fields |
Gain extra fields with no code |
| Tags | com_tags |
Tag items across components |
| ACL | #__assets |
Inherit site-wide permissions |
| Languages | language packs | Be fully translatable |
// A component opts INTO the shared category service in services/provider.php
$container->registerServiceProvider(new CategoryFactory('\\Joomla\\Component\\Example'));
An extension that consumes the shared Categories service instantly gets a nested hierarchy, ACL inheritance, and multilingual support - all for free. That shared service is a topic worth a focus of its own.
Back to top9. Common Mistakes and Pitfalls
9.1 Disabling Instead of Uninstalling
Disabling an extension stops it from running, but the code stays on disk and in the database. For security, that is not enough. If you no longer use an extension, uninstall it so its files and tables are removed.
9.2 Forgetting the Update Site
If you install an extension manually and it never shows an update notice, it probably has no update site registered. Check System → Update → Update Sites. An extension without an update site will silently stay on its old version.
9.3 Ignoring the Schema Warning
A "Database schema is out of date" warning is not cosmetic. It means an SQL migration did not run, often after a manual file update or an interrupted update. Go to Manage → Database and click Fix before strange bugs appear.
9.4 Installing from Untrusted Sources
"Nulled" or pirated copies of commercial extensions are a leading source of malware. The money you save is small compared to the cost of a hacked site. Always download from the developer or the JED.
9.5 Updating the Core but Not the Extensions
Many site owners run Joomla! Update faithfully but forget that extensions update separately. A patched core does not protect you from a vulnerable add-on. Check System → Update → Extensions as well.
9.6 Copying Files Without Discover
Uploading an extension's folder by FTP does not install it. Joomla only "knows" an extension when it has a #__extensions row. After uploading files, run Manage → Discover to register them, or simply install the zip the normal way.
10. Best Practices
If you only remember a few things from this article, remember these:
- Treat every component, module, plugin, template, and language as the same kind of thing: an extension.
- Install only from the JED or directly from the developer.
- Keep both the Joomla core and every extension up to date.
- Uninstall extensions you no longer use - disabled is not enough.
- Fix schema warnings as soon as they appear.
- When building, ask first whether an override or a custom field is enough before writing a new extension.
- Reuse Joomla's shared services (Categories, Fields, Tags, ACL) instead of reinventing them.
11. Quick Reference
INSTALL System → Install → Extensions (Upload / Folder / URL / Web)
MANAGE System → Manage → Extensions (enable / disable / uninstall)
UPDATE System → Update → Extensions
CORE UPDATE System → Update → Joomla (separate from extensions)
DISCOVER Manage → Discover (register files already on disk)
SCHEMA FIX Manage → Database (replay missing SQL)
WARNINGS Manage → Warnings (environment problems)
REGISTRY Every extension = one row in #__extensions
UNIQUE BY type + element + folder + client_id
OPTIONS Stored as JSON in #__extensions.params
Back to top12. Summary
In Joomla, "extension" is the umbrella word for everything you add to the CMS - and core Joomla itself ships as extensions. The famous five (component, module, plugin, template, language) are joined by the packaging types: package, library, and file.
Once you see the shared machinery, every type makes sense:
- One table: every extension is a single row in
#__extensions, identified bytype + element + folder + client_id. - One manifest: a self-describing XML file drives install, update, uninstall, configuration, and where updates come from.
- One installer:
com_installerruns the same pipeline for core and third-party code, with Discover, Database fix, and Warnings as its tools. - One update model: an update is just an upgrade install of a newer zip, separate from Joomla! Update for the core.
- One safety rule: install from trusted sources, keep everything updated, and remove what you do not use.
Great extensions opt into Joomla's shared services - Categories, Fields, Tags, ACL, and Languages - instead of reinventing them. That is what makes the Joomla ecosystem fit together so well.
If you are planning a new Joomla site, troubleshooting an extension that misbehaves, or worried that an outdated add-on is putting your site at risk, it pays to understand how extensions work. They are the building blocks of every Joomla site.
Back to top

Joomla en Linux specialist voor snelle, veilige en schaalbare websites.


