Upgrading Joomla 4 to 5
|
This content was generated with the assistance of AI and should be verified against the official Joomla documentation before being relied on in production. This sub-section’s Joomla bibliography lists the reference material consulted while preparing these pages. |
Joomla 5.0 is the second half of a two-year cycle. Joomla 4.4 closed the 4.x series without new features, and Joomla 5.0
is the first release that removes code deprecated in the 3.x era. This page explains how a site and an extension move
from 4.4 to 5.0: the preparation, the compatibility plugin that keeps old code running, everything that 5.0 removed or
changed, and how to recover when an upgrade goes wrong. The examples use the fictitious Tallpine Hiking Club package
pkg_tallpine with the component com_tallpine (namespace Tallpine\Component\Tallpine) and the child template
cassiopeia_tallpine.
The page was checked on 2026-10-30 against the Developer Manual update notes for 4.4 to 5.0 and the User Guide migration chapter. Joomla 5.0 was released on 2023-10-17, together with 4.4.0. The current release is Joomla 6.2.x (PHP 8.3+), 5.x is in its security-only period (5.4.10 was the last bugfix release; security fixes continue until 2027-10-12) and 4.x reached end of life in October 2025. A site that is still on 4.4 therefore has to cross two majors: this page gets it to 5.4, and Upgrading Joomla 5 to 6 takes it from there to 6.x. Practical reasons to read this page today are a legacy site, a client extension that still targets 4.x, and code that must be understood before it is moved forward. The version policy behind the schedule is in Versions, Release Cycle and Support; sites that start from 3.10 use Migrating from Joomla 3.
The path: 4.4 is the bridge
Joomla ships a major version every two years with four minors. The last minor of each major (x.4) is a bridge release: it adds no features and is the only supported starting point for the next major. Updating from 4.0, 4.1, 4.2 or 4.3 directly to 5.0 is not offered, and the Joomla Update component does not show a 5.x target for a site below 4.4. The 4.4 release also contains the deprecation notices and the compatibility code that make the jump predictable.
The same rule repeats at every major: update to the latest release of the bridge (4.4.x, later 5.4.x), then move to the next major. Downgrades are not supported, so the only way back from a failed upgrade is the backup.
Requirements for Joomla 5
The User Guide lists these prerequisites. If the server does not meet them, the Joomla 5 update option does not appear in the Joomla Update component:
| Component | Minimum for the 4.4 to 5.0 step |
|---|---|
Joomla |
4.4.x (earlier 4.x releases must be updated first) |
PHP |
8.1 |
MySQL |
8.0.13 |
MariaDB |
10.4.x |
PostgreSQL |
12.0 (PostgreSQL 16 needs a manual change before the update, described in the guide) |
Check the values under System → System Information. These are the 5.0 minimums; the floor rises again in 6.0 (PHP 8.3) and is covered in Requirements and Hosting. A host that supports PHP 8.3 and a current database lets the site go straight through 5.4 to 6.x without a second server change. Joomla also does not migrate between MySQL and PostgreSQL, and it cannot be downgraded.
Planning the upgrade of a site
The User Guide chapter recommends this order of work:
-
Confirm that hosting meets the requirements above.
-
Inventory the extensions. Treat the Extensions section of the pre-update check as a rough overview only; System → Manage → Extensions and each developer’s site are the reliable references. The Joomla Extensions Directory shows a Joomla 5 compatibility badge on listings.
-
Test the upgrade on a copy: restore a backup on a subdomain or locally, switch on debug, run the upgrade and disable every extension that throws errors. A second option is a clean Joomla 5 install with the compatibility plugin enabled into which the extensions are installed.
-
Review the component-specific changes: Cassiopeia remains the frontend template (test child templates and overrides); the Google reCAPTCHA plugin is removed;
com_searchdoes not exist in Joomla 5 (uninstall it and use Smart Search);com_weblinksworks without the compatibility plugin since 4.3.0; and the System - Schema.org plugin has to be configured with the site’s details after the upgrade. -
Repeat the trial until it runs without problems.
For the Tallpine site this means: update the 4.4 site to the latest 4.4.x, restore last night’s backup into a Docker Compose copy (see Local Development with Docker), run the upgrade there, and only then schedule the production window.
The upgrade procedure
The steps on the production site, in order:
-
Update every extension that has an update available and disable or uninstall the ones that are not compatible with Joomla 5. Uninstall
com_searchand its modules. -
Replace the reCAPTCHA plugin by another captcha, for example the invisible captcha or a third-party extension.
-
Enable System Debug in Global Configuration → System.
-
Take a backup, then restore it somewhere to prove it works (see Backups and Recovery).
-
Open System → Update → Joomla, then Options, set Update Channel to Joomla Next and save.
-
Read the Pre-Update Check. Its required and recommended settings and the extensions list only appear when the server meets the requirements.
-
Acknowledge the warnings and the backup prompt and start the update.
-
Check the version in the top right corner, test frontend and backend, switch debug off in Global Configuration → Server, fix the Smart Search settings and configure the Schema.org plugin.
The Joomla Next channel is the mechanism that offers the next major to a site that is on the latest bridge release. It is described together with the pre-update check and the TUF-signed packages in Updating Joomla and Automated Core Updates; that page is not repeated here. Core updates have been verified with The Update Framework (TUF) since 5.1, and Automated Core Updates arrived in 5.4 and 6.0, so neither exists in 5.0 itself. A site upgraded to 5.x keeps automated updates switched off until an administrator enables them.
The Behaviour - Backward Compatibility plugin
Joomla 5.0 introduced a system plugin of the Behaviour group, named Behaviour - Backward Compatibility (database
name plg_behaviour_compat). It is the main safety net of the upgrade. Most code that was deprecated in 3.x was removed
in 5.0; the plugin restores the parts that extensions still commonly use, so that a Joomla 4 extension keeps working,
and extensions that are not yet updated can still be installed in 5.x. The Manual describes the intention as giving
extension developers and site owners two more years to bring old code up to date.
The plugin has three independent options that a Super User can switch separately:
| Option | What it does |
|---|---|
Load legacy class name aliases |
Registers the old |
Load es5 webassets shim |
Re-registers the web asset names that ended in |
Load removed webassets shim |
Provides an empty entry for |
After an update from 4.4 the plugin is enabled automatically with all three options on. New installations of 5.0 also have it enabled. The plugin is planned to go away: code that depends on it is removed for good in Joomla 6, where it is replaced by Behaviour - Backward Compatibility 6 (BC6) with a different scope, as described in Upgrading Joomla 5 to 6.
Why try to disable it
The plugin has a measurable cost on every request. The Manual gives these numbers: the class alias option adds more than 500 PHP function calls per request and parses more than 1000 lines of JSON data, while the web asset shims add only about 15 lines. Disabling the two web asset options is safe in practice, because they only matter if an extension loads a legacy web asset directly. The class alias option is trickier, because the aliases cover twenty years of class names. Disabling the plugin is also the only reliable way to find out which extensions will fail in 6.x, so the Manual urges extension developers to test with it off.
Class aliases that are used in the core or stored in the database cannot be moved to the plugin and remain available
without it; the example the Manual gives is JComponentHelper for \Joomla\CMS\Component\ComponentHelper, used in the
filterText XML field definitions.
Two rules for the plugin
-
Do not disable it on production first. Disable it on a test copy, with access to the database, and watch for PHP errors. A class that is no longer found produces a fatal error that can take the administrator down too.
-
Never create your own plugin in the
behaviourgroup. The Manual warns that the group itself may be removed. Use thesystemgroup or a purpose-specific group instead.
Recovering after disabling the plugin
If disabling the plugin locks you out of the administrator, set the enabled column back to 1 in the extensions table
for the row named plg_behaviour_compat. The table prefix is chosen at install time; jos_ below is a placeholder.
-- Re-enable Behaviour - Backward Compatibility (replace jos_ with your table prefix)
UPDATE jos_extensions
SET enabled = 1
WHERE element = 'compat'
AND folder = 'behaviour'
AND type = 'plugin';
In the Manual’s wording the row is the one named plg_behaviour_compat, in the table that appears in documentation as
#__extensions. A problematic extension can be switched off the same way, by setting its enabled field to 0 and then
uninstalling it from the administrator.
What 5.0 removed
The page Removed and Backward Compatibility of the update notes is the authoritative list. This section groups it by what a site owner or developer has to do.
Packaging: .tar.bz2 replaced by .tar.zstd
From the final 5.0.0 release the project no longer builds .tar.bz2 download packages. It builds .tar.zstd archives
instead, which are about 3 MiB smaller than the bzip2 packages and about 11 MiB smaller than the zip package. Scripts
that download and unpack a release (Dockerfiles, deployment pipelines) must change the extraction command, and the
build image needs zstd support. The .zip package is still published.
# Tallpine deployment step: unpack a release package into the web root
mkdir -p /var/www/html
tar --zstd -xf Joomla_5.4.10-Stable-Full_Package.tar.zst -C /var/www/html
JavaScript and CSS: ES5 files and removed assets
The build tools no longer create the transpiled *-es5.js files, and the dependencies that pointed at them were
removed from the joomla.asset.json files. The ES5 entries remain registered for backward compatibility, with empty
URLs, and the compatibility plugin supplies the shim described above. The .es6.js files are transpiled to ES2018, and
the minimum browser versions are the ones listed by caniuse for ES modules. Third-party code that used
type="module" and nomodule to serve modern and legacy code still works, but the core no longer ships IE11-compatible
scripts. The .ie11 CSS class was also removed.
Two JavaScript files were deleted outright, together with their -es5.js, minified and gzipped variants and their
joomla.asset.json entries:
-
media/com_templates/js/admin-template-compare.js, unused by the core since 4.3.0 (the side-by-side comparison in the template manager) -
media/com_users/js/admin-users-mail.js, unused by the core since 4.3.0
Joomla 4 kept them so that layout overrides written against the old layouts kept working. An override copied from a 4.x core layout that still loads one of these files has to be rebuilt from the 5.x layout: follow the change the core made to the matching layout. A Tallpine override of the users mail layout, for instance, must be compared with the new core layout rather than patched. How assets are registered and loaded is covered in Web Asset Manager; override updating is covered in Template Overrides and Alternative Layouts.
Wincache, LDAP and other libraries
-
Wincache is removed without replacement: the extension is abandoned and not supported by PHP 8. Sites that selected the WinCache cache handler or session handler must choose another handler before the upgrade (see Caching and Performance).
-
Joomla\Ldapis removed because Joomla’s own LDAP library is no longer compatible with 5.0. The aliasesJClientLdapandJLDAP(both pointing at\Joomla\Ldap\LdapClient) were removed from the compatibility plugin as part of this. Joomla 4 and 5 ship the Symfony LDAP package, which is what code should use. -
PSR-3 upgraded to version 3. The
$messageargument of the\Psr\Log\LoggerInterfacemethods is now typedstring|\Stringable. A custom logger, or a logger library such as Monolog, must be compatible. An extension that has to run on both 4.x and 5.x either keeps a private copy of the interface under its own namespace or loads the correct logger version per Joomla version. -
Joomla Framework 3.0. The dependency container can now override services in child containers, which allows an extension to override protected services such as the form factory when it boots.
Plugins removed or reshaped
-
reCAPTCHA plugin: the outdated Google reCAPTCHA plugin is removed. The invisible captcha plugin is unlocked, so it can be uninstalled by hand, and the Manual recommends installing a new captcha from the Joomla Extensions Directory. Review Global Configuration and every form (contact, registration, login) that selected it. Joomla 6.1 later added a Proof-of-Work captcha to core.
-
Demo task plugin: the demonstration scheduled task plugin is gone (see Scheduled Tasks).
-
CodeMirror 6: the editor plugin is now based on CodeMirror 6 with ES modules. Customisation and JavaScript written for CodeMirror 5 is incompatible.
-
TinyMCE: CSS classes defined in a template’s
editor.cssno longer appear in the formats menu; enable the new CSS Classes Menu option to show them. -
Captcha, editor and editors-xtd plugins must use the
onCaptchaSetup,onEditorSetupandonEditorButtonsSetupevents. Legacy plugins keep working until the next major.
A captcha plugin for 5.x registers a provider in the setup event. The provider methods were renamed: onDisplay
became display(string $name = '', array $attributes = []): string, onInit was removed (load assets in display()),
onCheckAnswer became checkAnswer(string $code = null): bool, and onSetupField became
setupField(FormField $field, \SimpleXMLElement $element): void.
<?php
namespace Tallpine\Plugin\Captcha\Honeypot\Extension;
use Joomla\CMS\Captcha\CaptchaProviderInterface;
use Joomla\CMS\Event\Captcha\CaptchaSetupEvent;
use Joomla\CMS\Form\FormField;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
\defined('_JEXEC') or die;
final class Honeypot extends CMSPlugin implements SubscriberInterface, CaptchaProviderInterface
{
public static function getSubscribedEvents(): array
{
return ['onCaptchaSetup' => 'onCaptchaSetup'];
}
public function onCaptchaSetup(CaptchaSetupEvent $event): void
{
// The plugin implements the provider itself.
$event->getCaptchaRegistry()->add($this);
}
public function display(string $name = '', array $attributes = []): string
{
return '<input type="text" name="tallpine_hp" value="" tabindex="-1" autocomplete="off" class="visually-hidden">';
}
public function checkAnswer(string $code = null): bool
{
return $this->getApplication()->getInput()->post->getString('tallpine_hp', '') === '';
}
public function setupField(FormField $field, \SimpleXMLElement $element): void
{
$element['hiddenLabel'] = 'true';
}
}
The class above is deliberately small; the provider interface needs the full method set of the release it targets, and the
plugin also needs its manifest and services/provider.php, shown in
Plugin Development and Events.
Smaller removals and behaviour changes
-
The
resynctask of the administratorMenusControlleris removed; it only served the 1.5 to 1.6 upgrade routine. -
The "Cannot load user X" message for deleted users was removed.
-
The development choice of the error reporting option in Global Configuration was removed as obsolete, because
error_reporting(-1)andE_ALLbehave the same since PHP 5.4. -
The toolbar
popupButtonrenders with the new JoomlaDialog script, while legacy Bootstrap modals still work. -
The links in
com_contentcategory blog links and featured links layouts use<ul>instead of<ol>, which matters for CSS that targeted ordered lists in overrides. -
\Joomla\CMS\MVC\View\CategoryFeedViewextendsAbstractViewdirectly instead ofHtmlView. -
The input object is available in the DI container, but extensions should keep reading input from the application object.
\Joomla\CMS\Factory::$applicationis now set reliably in each application’sincludes/app.php. -
A custom
JoomlaStoragesession object takes the cookie domain and path from its options and no longer reads them from the application object.
Changes that affect extension code
Return types
All return types of core classes were updated to match the PHP 8.1 signatures. This concerns classes that implement
ArrayAccess, DateTime or JsonSerializable. The Manual lists the affected files:
| Class | Changed signatures |
|---|---|
|
|
|
|
|
|
|
|
|
now extends |
A class of your own that extends one of these must declare compatible return types, otherwise PHP raises a fatal error
at load time. To support Joomla 4.x and 5.x with one code base, add the #[\ReturnTypeWillChange] attribute, which older
PHP versions ignore and newer ones treat as an opt-out of the deprecation notice. Declaring the real type is possible
only after dropping 4.x.
<?php
namespace Tallpine\Component\Tallpine\Administrator\Feed;
use Joomla\CMS\Date\Date;
\defined('_JEXEC') or die;
/**
* Trail date that stays compatible with Joomla 4.4 and 5.x.
*/
class TrailDate extends Date
{
#[\ReturnTypeWillChange]
public function format($format, $local = false, $translate = true)
{
return parent::format($format, $local, $translate);
}
}
The model must receive the database in its constructor
Since 5.0 the database instance of a model has to be injected through the constructor configuration, so that the base
class knows it and getDatabase() and the deprecated getDbo() point at the same object. A model that used to fetch its
database by itself and ignore the base class gets a different instance than the base class uses. In the Manual’s
pattern the model calls setDatabase() after parent::__construct(). The modern form is to use the MVC factory, which
creates the model and gives it the database from the container (see
Namespaces, Autoloading and Dependency Injection
and Database API).
<?php
namespace Tallpine\Component\Tallpine\Administrator\Model;
use Joomla\CMS\MVC\Model\ListModel;
use Joomla\Database\DatabaseInterface;
\defined('_JEXEC') or die;
class TrailsModel extends ListModel
{
public function __construct($config = [], $factory = null, ?DatabaseInterface $database = null)
{
parent::__construct($config, $factory);
// An external database (for example a read-only replica for reports)
if ($database !== null) {
$this->setDatabase($database);
}
}
protected function getListQuery()
{
$db = $this->getDatabase();
$query = $db->createQuery()
->select($db->quoteName(['a.id', 'a.title', 'a.distance_km']))
->from($db->quoteName('#__tallpine_trails', 'a'))
->where($db->quoteName('a.state') . ' = 1');
return $query;
}
}
The current user: Factory::getUser() and CurrentUserInterface
Factory::getUser() has been deprecated since 4.0. Models, views, form fields and tables can implement
CurrentUserInterface, and the logged-in user is then available through $this->getCurrentUser(). The core
template files (default.php and the other layouts) were switched in 5.0, so a layout override that still calls
Factory::getUser() works but is out of step with the core layout and with 7.0 plans.
<?php
use Joomla\CMS\Factory;
\defined('_JEXEC') or die;
// Joomla 4.4 style, deprecated
$user = Factory::getUser();
// Joomla 5 style, inside a layout of a view that uses CurrentUserInterface
$user = $this->getCurrentUser();
if ($user->authorise('core.edit', 'com_tallpine')) {
echo '<a class="btn btn-primary" href="#">Edit trail</a>';
}
Form field classes
The old-style JFormField* classes have no equivalents in 5.0 outside the compatibility plugin. Code extends the
namespaced classes, for example \Joomla\CMS\Form\Field\ListField and \Joomla\CMS\Form\Field\TextField. The namespaced
classes do not exist in Joomla 3, so an extension that must run on 3 and 5 without the plugin has to bring its own base
class. Field classes are covered in Forms and Form Fields.
Core classes no longer extend CMSObject
Nine core classes stopped extending CMSObject: CategoryNode, Changelog, the Stream of the CMS filesystem
package, InstallerExtension, BaseModel, AbstractView, Table, Update and User. They include the
LegacyErrorHandlingTrait and LegacyPropertyManagementTrait, so behaviour is unchanged, but the traits are deprecated:
throw exceptions and write getters and setters. Model state is now a Registry based class instead of a CMSObject, and
ContentHelper::getActions() returns a Registry too. Direct property access still works through magic methods that
raise deprecation warnings. The detailed story, including what disappeared in 6.0, is in
Error Handling and Legacy APIs; this page does not repeat it.
<?php
// Joomla 4.4: state as an object property
$ordering = $this->state->list_ordering;
// Joomla 5: state is a Registry
$ordering = $this->getModel()->getState()->get('list.ordering');
$model->getState()->set('filter.season', 'summer');
// Actions
$canCreate = \Joomla\CMS\Helper\ContentHelper::getActions('com_tallpine')->get('core.create');
Plugin constructors and event classes
The CMSPlugin constructor no longer contains the extra assignment operator for the dispatcher. A plugin that has extra
constructor arguments is now written like this:
public function __construct(DispatcherInterface $dispatcher, array $config, TrailRepository $trails)
{
parent::__construct($dispatcher, $config);
$this->trails = $trails;
}
Joomla 5.0 also gave the system, content, user, finder, module, installer, privacy, custom fields, action log, cache,
contact, check-in, menu and com_ajax events their own event classes, with getters instead of reference parameters. The
old forms keep working, with a deprecation, and the Manual states that by-reference modification will be removed in a
later release. For example, onRenderModule used to receive $attributes by reference; now it receives a
BeforeRenderModuleEvent:
// Old: modification by reference
public function onRenderModule($module, &$attributes)
{
$attributes['class'] = 'tallpine-banner';
}
// New: Joomla 5
public function onRenderModule(\Joomla\CMS\Event\Module\BeforeRenderModuleEvent $event)
{
$attributes = $event->getAttributes();
$attributes['class'] = 'tallpine-banner';
$event->updateAttributes($attributes);
}
The same notes list the conversions for onAfterRenderModules, the module list events, the custom fields,
installer, content, contact, menu and user group events. The mechanics of subscribing to them, and the 6.x state of the
legacy forms, are explained in Plugin Development and Events. Older
books such as Developing Extensions for Joomla! 5 (2023) show plugins that read $event->getArgument() arrays and use
a $db plugin property; both still work in 5.0 but are the legacy form, and the automatic $app and $db detection is
removed in 7.0.
New in 5.0
Not everything in the release is a removal. The update notes list these additions that an upgraded site can use:
-
The public folder. The site can be served from a
publicfolder (a subfolder of the Joomla root or elsewhere), with command line tools for the setup. The requirements are that templates support child templates, that no PHP files live inmediaandimages, and that the server can set the document root. See Installation and the Public Folder. -
Event classes for most core events, as described above, and the new system event
onAfterInitialiseDocument. -
ESM import maps in the Web Asset Manager and the JoomlaDialog script for modals.
-
Dark mode in the Atum administrator template, driven by
prefers-color-scheme. Templates that follow it add thedata-color-scheme-osattribute to<body>so editors can load a matching theme. -
Schema.org support, which has to be configured after the upgrade (see SEO, SEF URLs, Redirects and Schema.org).
Common errors and pitfalls
| Symptom | Likely cause and remedy |
|---|---|
The Joomla 5 update is not offered |
The site is below 4.4, the channel is not Joomla Next, or PHP or the database is below the requirements. Check System Information and the pre-update check. |
White page after disabling the compatibility plugin |
An extension uses a class alias. Set |
Fatal error: declaration must be compatible |
A class extending |
Logger fatal error after the upgrade |
The logger does not match PSR-3 3.0 (the |
Captcha field missing on forms |
The reCAPTCHA plugin was removed. Install and configure another captcha and set it in Global Configuration. |
An administrator screen fails to load an asset |
A layout override still loads a removed script such as |
Cache or session handler error on a Windows server |
WinCache was selected before the upgrade. Switch the handler in Global Configuration before updating. |
Different database in model and base class |
The model assigned its own database without |
Security notes
-
An upgrade changes security-relevant defaults: the removed reCAPTCHA plugin means unprotected forms until a replacement is configured, so check every form that used it immediately after the update.
-
Keep the compatibility plugin only as long as needed. It loads code that nothing maintains, and the aliases hide calls to deprecated APIs. 5.x gets security fixes only until 2027-10-12, so the plugin is a bridge to 6.x, not a destination.
-
A 4.x site has received no security fixes since October 2025. Treat a 4.4 site that cannot reach 5.4 quickly as an incident to schedule, not a maintenance item. Hardening measures (MFA, passkeys, HTTP headers) are in Security Hardening, MFA and Passkeys.
-
Never run the upgrade on production without a restorable backup. Debug mode that was enabled for the upgrade must be switched off afterwards.
Corrections to older material
-
Joomla! 4 Masterclass (2023) targets Joomla 4.x, which is end of life since October 2025, and has nothing about 5.0 and later. Use this page and the Manual for the 4.4 to 5.0 steps.
-
Developing Extensions for Joomla! 5 (2023) is written for 5.0 and shows
$dbas a plugin property, genericgetArgument()events and a non-namespaced plugin class in places; since 5.0 the event classes andSubscriberInterfaceare the supported form. -
Wiki pages on docs.joomla.org mark themselves as obsolete; the User Guide and the Developer Manual are the current documentation.
-
Material that says the compatibility plugin is permanent is wrong: it was announced as temporary and replaced in 6.0.