Joomla Web Asset Manager
|
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. |
Every Joomla page is made of HTML plus a set of stylesheets and scripts. Joomla 4 replaced the old habit of calling
addStyleSheet() and addScript() from anywhere with the Web Asset Manager: a registry of named assets (a style, a
script or a preset), each with declared dependencies, which extensions and templates use by name and which the
document renders into the page head in dependency order. This page explains the whole mechanism: the
joomla.asset.json file format, the five registry files and the order in which Joomla reads them, the PHP API
(useStyle(), useScript(), usePreset() and friends), inline assets, versioning and cache busting, ES modules and
import maps, web components, how to override or disable a core asset, the output attributes (defer, type="module"),
the security implications and the errors you will meet. It was checked against the official Developer Manual and the 6.2
source tree on 2026-10-30. The behaviour described is that of Joomla 6.2 (current, PHP 8.3 or newer); where Joomla 5.4
(security fixes only) differs, the text says so. Joomla 4.x and 3.x are end of life.
The examples belong to the Tallpine Hiking Club, the fictitious mountain club used across this sub-section. Its
child template cassiopeia_tallpine (see Child templates) needs its own
stylesheet and script, a trail-map ES module, a fallback for old browsers, a front-end module mod_tallpine_nexthikes
with its own CSS, and a way to switch off a core asset it does not use. The site is built step by step across the
sub-section; this page is the part about assets. The accessibility consequences of scripts and styles (focus order,
reduced motion, contrast) are covered in the general Accessibility page and, for Joomla
templates, in Colour modes, RTL and accessibility.
What the Web Asset Manager is, and who uses it
The Web Asset Manager (WAM) is a PHP service, Joomla\CMS\WebAsset\WebAssetManager, attached to every HTML document.
It sits on top of a WebAssetRegistry that knows every asset that can be loaded, and it keeps track of the assets
that will be loaded on the current request. Four kinds of people touch it:
-
The themer writes a
joomla.asset.jsonfor the template and calls the manager from the template’sindex.php. -
The extension developer ships a
joomla.asset.jsonin the extension’s media folder and calls the manager from a view, a layout, a module dispatcher or a plugin. -
The site builder rarely edits assets directly but sees them in the page source, in the debug bar and when a template override breaks a page.
-
The administrator of a performance- or security-minded site decides which core assets to keep, how they are cached and how they are allowed under a Content Security Policy.
Older material predates it. Tutorials written for Joomla 3 call JHtml::_('jquery.framework'),
JHtml::script() or $document->addScript(); the Joomla 4 Developer Manual calls the Document::addScript() and
Document::addStyleSheet() family deprecated, to be removed in Joomla 6, and tells you to use the manager instead.
The book Joomla! 4 Masterclass (2023) was written for Joomla 4.x and so predates import maps, the removal of the ES5
assets and the nocache version of Joomla 6.1; Developing Extensions for Joomla! 5 (2023) covers the manager itself
well but was written for Joomla 5.0 and PHP 8.2, so its version statements need the corrections that follow in this
page. What has not changed is the important part: Text::script() and Document::addScriptOptions() are not
deprecated and remain the way to hand translated strings and variables to JavaScript.
The three stages: register, use, render
Everything the manager does happens in three stages.
-
Register. While the application boots, Joomla reads a series of
joomla.asset.jsonfiles into the registry. A registered asset is only a description (name, type, file, dependencies, attributes); nothing is output for it. -
Use. Code asks the manager to activate assets by name, for example
useScript('field.modal-fields'). The manager marks the asset active and, lazily, every asset it depends on as active as a dependency. Using a name that is not registered throws anUnknownAssetException. -
Render. When the template’s head is built, the
<jdoc:include type="styles" />and<jdoc:include type="scripts" />tags are replaced by the<link>and<script>elements of the active assets, sorted so that dependencies come first, with their attributes and a version query string.
The two renderers matter because they explain a frequent surprise: an asset activated after the head has been
rendered is never output. In a template the <jdoc:include> tags are replaced after index.php ran, so calling
useScript() anywhere in index.php, in a layout or in a module is early enough. A call made from a plugin that runs
after onBeforeCompileHead is not.
The joomla.asset.json file
A registry file is plain JSON. The $schema property points at the schema Joomla publishes for editors, and the
top-level object has a name, a version, a description, a license, an optional namespace (explained under
custom asset classes) and the assets array.
Each entry of assets is an object. The keys you will use are:
| Key | Meaning |
|---|---|
|
The registry key. Required. A style and a script may share a name, because the registry is keyed by type and name. |
|
Required: |
|
The file, relative to the extension’s media folder and without the |
|
|
|
An array of asset names to use first. In a preset the entries are suffixed |
|
Attributes copied onto the generated element: |
|
A number used to order assets that have no dependency relation; the core template uses |
|
Marks the asset deprecated. In debug mode ( |
|
Add a script to the page’s import map (see the ES modules section). |
|
Choose a custom |
Keys that the manager does not interpret are kept as options of the asset and passed along, which is how
importmap, weight, deprecated and version work. Unrecognised keys therefore do no harm, but they do nothing
either. A missing name or type makes the whole file fail with a RuntimeException, and so does invalid JSON: a
stray comma in the template’s joomla.asset.json takes the front end down on every page, not just on the page that uses
the asset.
A first file for the child template
The cassiopeia_tallpine child template ships a stylesheet that sets the club’s colours and a script that adds a
"back to the trailhead" link behaviour. It keeps its assets in its own media folder, media/templates/site/cassiopeia_tallpine,
which the template manifest installs from its media folder.
{
"$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json",
"name": "cassiopeia_tallpine",
"version": "1.0.0",
"description": "Assets of the Tallpine Hiking Club child template.",
"license": "GPL-2.0-or-later",
"assets": [
{
"name": "template.tallpine",
"description": "Club colours, typography and trail badges.",
"type": "style",
"uri": "tallpine.css",
"dependencies": [
"template.cassiopeia.ltr#style"
]
},
{
"name": "template.tallpine",
"description": "Small enhancements: back to trailhead, collapsible weather strip.",
"type": "script",
"uri": "tallpine.js",
"attributes": {
"type": "module"
},
"dependencies": [
"core"
]
},
{
"name": "template.tallpine",
"type": "preset",
"dependencies": [
"template.tallpine#style",
"template.tallpine#script"
]
}
]
}
Source: Joomla Manual: Web Asset Manager
The file lives in the template root (templates/cassiopeia_tallpine/joomla.asset.json) and must be listed in the
<files> block of templateDetails.xml like any other template file; the CSS and JavaScript live under
media/templates/site/cassiopeia_tallpine/css/ and .../js/. The three entries share one name, template.tallpine.
That is legal because the registry is keyed by type: one name is a style, a script and a preset at the same time, and
usePreset('template.tallpine') activates the style and the script together.
The core template follows the same pattern. Cassiopeia’s own file registers template.cassiopeia.ltr and
template.cassiopeia.rtl styles, a template.cassiopeia module script that depends on core, two dummy
template.active assets that extensions can depend on to load "after the active template", a template.user style and
script for the site owner’s user.css and user.js, and presets that pair the right-to-left or left-to-right style
with the script. Reading that file is the best documentation of the idea.
Where assets are found: the registry lookup order
Joomla does not scan the file system for asset files. It reads a fixed list of registry files, in a fixed order, and later definitions replace earlier ones with the same type and name. The order is:
-
media/vendor/joomla.asset.json: third-party libraries bundled with Joomla (loaded the first time the registry is used). -
media/system/joomla.asset.json: Joomla’s own core assets (core, form validation, the dialog, the editors and so on). -
media/legacy/joomla.asset.json: assets kept for backwards compatibility. -
media/<component>/joomla.asset.json: the active component’s file, for examplemedia/com_tallpine/joomla.asset.json, added when the component is dispatched. -
templates/<template>/joomla.asset.json: the active template’s file. For a child template Joomla adds the parent’s file first and then the child’s, so the child can override the parent.
Two consequences follow. First, a template can override any asset a component or the core registered, simply by
declaring one with the same name, which is what the overriding section below uses. Second, anything that is not on the
list is not loaded automatically. Modules, plugins and layouts that ship a joomla.asset.json must add it
themselves with addRegistryFile(), because the manager only reads the files of the active component and template.
A module author who forgets this sees an UnknownAssetException as soon as useStyle('mod_tallpine_nexthikes') runs.
The paths given to addRegistryFile() are relative to the site root, for example
media/mod_tallpine_nexthikes/joomla.asset.json. A site that uses the public folder (see
Installation and the public folder) still writes the same
relative path.
Where the asset files themselves come from
The uri of an asset is resolved when the page is rendered. For a relative uri, Joomla looks at the template’s media
folder first (the child’s, then the parent’s), then at the extension’s media folder. In particular:
-
tallpine.cssregistered by the template resolves tomedia/templates/site/cassiopeia_tallpine/css/tallpine.css, and a file with the same name in the parent’s media folder is used if the child has none. -
com_tallpine/trail-map.cssresolves tomedia/com_tallpine/css/trail-map.css. -
system/joomla-fontawesome.min.cssresolves tomedia/system/css/joomla-fontawesome.min.css. -
A
uriwhose file exists under the site root (it contains a/and the file is found there) is treated as an absolute path from the site root; anything starting withhttp://,https://or//is external and left alone.
There are two debug-related behaviours. When debug is off, a request for template.css loads template.min.css if it
exists; when JDEBUG is on, the unminified file is loaded instead, which is why the core ships both. And because a
template’s media folder is searched before the extension’s, a template can replace a core file by placing a file at the
same relative path under its own media folder (for example css/system/searchtools/searchtools.min.css) without
touching the registry at all. That is a file override, whereas redefining the asset is a registry override; the
overriding section compares them.
Using assets from PHP
The manager is obtained from the document. In a template, the document is $this; elsewhere you ask the application.
<?php
use Joomla\CMS\Factory;
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
Source: Joomla Manual: Web Asset Manager
Inside a class that already has the application (a dispatcher, a plugin with getApplication(), a view with
$this->getDocument()), prefer the injected application or document to the static Factory; the call chain is the
same.
The calls are generated from three verbs and three types. use, disable, register and registerAndUse combine with
Style, Script and Preset, and all return the manager so they can be chained.
| Call | Effect |
|---|---|
|
Activates a registered asset. Its dependencies are activated as dependencies. |
|
Deactivates an asset that was activated directly. A dependency of another active asset is brought back. |
|
Adds or replaces a registry entry at run time. |
|
Registers and activates in one call. |
|
Adds an inline |
|
Introspection. |
The state of an asset is therefore one of inactive, active or dependency. Dependencies are resolved lazily, the
first time the manager is asked for its assets or for a state, and again whenever the set of active assets changes.
Resolution is recursive; an asset that lists itself is ignored; a missing dependency throws
UnsatisfiedDependencyException.
In the child template’s index.php
The child template overrides index.php of Cassiopeia (see
Template overrides and alternative layouts) and adds its
own assets to the ones Cassiopeia activates.
<?php
// templates/cassiopeia_tallpine/index.php (excerpt)
\defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
/** @var Joomla\CMS\Document\HtmlDocument $this */
$wa = $this->getWebAssetManager();
// Parent template assets (the preset pairs the stylesheet with template.js)
$wa->usePreset('template.cassiopeia.' . ($this->direction === 'rtl' ? 'rtl' : 'ltr'))
->useStyle('template.active.language')
->useStyle('template.user')
->useScript('template.user');
// Child template assets, declared in templates/cassiopeia_tallpine/joomla.asset.json
$wa->usePreset('template.tallpine');
// Colour scheme chosen in the template style: register and use a file of the child's media folder
$scheme = $this->params->get('colourScheme', 'forest');
$wa->registerAndUseStyle(
'theme.tallpine.' . $scheme,
'global/' . $scheme . '.css',
['version' => 'auto']
);
Source: Joomla Manual: Web Asset Manager
Two details come from how Cassiopeia itself is written. It registers the colour theme with registerAndUseStyle() using
a file name taken from a template parameter, and it re-registers the dummy template.active style so that its
dependency is the left-to-right or right-to-left preset; every extension that depends on template.active therefore
loads after the right stylesheet. A child that replaces the stylesheet should keep that contract.
In a component view or layout
A component registers its assets in the component’s own joomla.asset.json (media/com_tallpine/joomla.asset.json,
installed from the extension’s media folder) and activates them from the view. The file is added automatically
because com_tallpine is the active component.
{
"$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json",
"name": "com_tallpine",
"version": "1.0.0",
"description": "Assets of the Tallpine trails component.",
"license": "GPL-2.0-or-later",
"assets": [
{
"name": "com_tallpine.trail-list",
"type": "style",
"uri": "com_tallpine/trail-list.css"
},
{
"name": "com_tallpine.trail-map",
"type": "script",
"uri": "com_tallpine/trail-map.js",
"attributes": {
"type": "module"
},
"dependencies": [
"core",
"com_tallpine.map-style"
]
},
{
"name": "com_tallpine.map-style",
"type": "style",
"uri": "com_tallpine/trail-map.css"
}
]
}
<?php
// components/com_tallpine/tmpl/trails/default.php (excerpt)
\defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
/** @var Tallpine\Component\Tallpine\Site\View\Trails\HtmlView $this */
$wa = $this->getDocument()->getWebAssetManager();
$wa->useStyle('com_tallpine.trail-list')
->useScript('com_tallpine.trail-map');
// Translated strings and data for the module script, never concatenated into inline JavaScript
Text::script('COM_TALLPINE_MAP_LOADING');
Text::script('COM_TALLPINE_MAP_NO_TRAILS');
$this->getDocument()->addScriptOptions('com_tallpine.map', [
'center' => [46.52, 8.01],
'zoom' => 11,
'api' => 'index.php?option=com_tallpine&view=trails&format=json',
]);
Source: Joomla Manual: Web Asset Manager
Naming assets with the extension prefix (com_tallpine.trail-map) avoids clashes in the shared registry: the last
registration of a name wins, so a generic name such as map can silently replace someone else’s asset.
In a module
A module is not scanned automatically, so its dispatcher registers its file before using it.
<?php
// modules/mod_tallpine_nexthikes/src/Dispatcher/Dispatcher.php (excerpt)
namespace Tallpine\Module\NextHikes\Site\Dispatcher;
\defined('_JEXEC') or die;
use Joomla\CMS\Dispatcher\AbstractModuleDispatcher;
class Dispatcher extends AbstractModuleDispatcher
{
protected function getLayoutData(): array
{
$data = parent::getLayoutData();
$wa = $this->getApplication()->getDocument()->getWebAssetManager();
$wa->getRegistry()->addRegistryFile('media/mod_tallpine_nexthikes/joomla.asset.json');
$wa->useStyle('mod_tallpine_nexthikes');
return $data;
}
}
Source: Joomla Manual: Web Asset Manager
The module’s own file has a single style entry named mod_tallpine_nexthikes. Writing modules is covered in
Module development and layout-side details in
Layouts and module chromes.
Presets
A preset is an asset of type preset whose job is to group others. Its dependencies list names of other assets, each
followed by the type to use (name#style or name#script); a preset has no file, so its uri is empty. Activating the
preset activates every listed item. The manager methods are usePreset(), disablePreset(), registerPreset() and
registerAndUsePreset().
Presets answer a real maintenance problem: Cassiopeia needs a style and a script that always go together, and the left
to right and right to left variants differ only in the style. Two presets keep that decision in one place. For the club’s
template, template.tallpine does the same for its style and script. If an entry of a preset cannot be found, the
manager throws UnsatisfiedDependencyException when the preset is used, not when it is registered. Presets are not
sorted: their items are activated in the order listed, and the ordinary dependency sort then arranges the resulting
assets.
Dependencies and ordering
The order of elements in the page is computed, not written. For each type, the manager takes the active assets plus
their dependencies and sorts them with a topological sort: a dependency always comes before the asset that needs it, and
assets with no relation to each other come out in the order they were activated (first in, first out), nudged by the
weight option when one is present. Cassiopeia gives the owner’s user.css a weight of 500 so that it is last. This
has practical rules:
-
Declare what you need in
dependencies, not by activation order. A script that usesJoomla.Textdepends oncore; a script that uses a form-validation helper depends onform.validate; a script that needs jQuery depends onjquery. jQuery is not loaded by default in Joomla 4 and later, which differs from the Joomla 3 behaviour that tutorials assume. -
A dependency cycle cannot be sorted. Keep the graph a tree.
-
The
template.activedummy asset is the supported way to say "load after the template’s stylesheet".
Versions and cache busting
Every asset has a version that controls the query string appended to its URL, so that browsers and proxies fetch a new file after an update.
version |
Output |
|---|---|
|
The document’s media version is appended ( |
|
A fresh value of the form |
any other string |
Appended as is, for example |
The query string is added only if the URL does not already contain a ?. The same rule applies to the src of the
import map. For nocache the manifest is simply "version": "nocache":
{
"name": "template.tallpine.dev",
"type": "style",
"uri": "tallpine-dev.css",
"version": "nocache"
}
Source: Joomla Manual: Web Asset Manager
The nocache value is a development tool. In production it defeats browser caching for that asset and, because the URL
changes on every request, a reverse proxy or CDN cannot cache the page’s references to it consistently either; remove it
before going live (see Caching and performance). A template that must also run on
Joomla 5.4 should not rely on the value there, because the 6.1 release notes and Manual place it in the 6.x line; set a
fixed version through registerAndUseStyle(..., ['version' => ...]) from PHP for that case.
Attributes: defer, async, module and nomodule
Everything in attributes is copied to the element. The commonly useful ones are:
-
"defer": trueor"async": trueon a script. Joomla’s own scripts are mostly deferred or modules; a script withtype: moduleis deferred by the browser anyway. -
"type": "module"marks an ES module. Cassiopeia’stemplate.cassiopeiascript uses it. -
"nomodule": truemarks a script that only browsers without module support execute. -
"integrity"and"crossorigin"for subresource integrity on a file from another host. -
"rel": "lazy-stylesheet"on a style, which Cassiopeia uses for Font Awesome. Joomla’s own script turns it into a normal stylesheet once the page has loaded, so the icons do not block rendering. A site that does not load that script must not use the value. -
data-*attributes read by the script.
{
"name": "tallpine.trail-widget.legacy",
"type": "script",
"uri": "com_tallpine/trail-widget-legacy.js",
"attributes": {
"nomodule": true,
"defer": true
}
},
{
"name": "tallpine.trail-widget",
"type": "script",
"uri": "com_tallpine/trail-widget.js",
"attributes": {
"type": "module"
},
"dependencies": [
"tallpine.trail-widget.legacy"
]
}
Source: Joomla Manual: Web Asset Manager
This module-plus-legacy-fallback pattern is how Joomla itself shipped assets in Joomla 4. In Joomla 5.0 the project
dropped the ES5 builds of its own assets: modern browsers all understand modules, so the nomodule variants of core
assets were removed. The consequences are practical. Third-party extensions
and old templates that depended on a core ES5 asset by name must stop; new assets should be written as modules and need
no fallback unless the club genuinely supports a browser without module support, which is not the case for any browser
Joomla 6 itself supports. Where you still use a nomodule script, keep it a leaf dependency of the module, as above.
In a debug session core assets carry data-asset-name and data-asset-dependencies attributes so you can see which
asset produced which element.
For script assets, a boolean attribute written as true is output as a bare attribute. If a template needs a
non-boolean attribute at run time, $wa->getAsset('style', 'fontawesome')->setAttribute('rel', 'lazy-stylesheet')
sets it on the registry item, as Cassiopeia’s own index.php does.
Inline assets
Sometimes the content is not a file: a few CSS custom properties computed from template parameters, or a three-line
initialisation script. addInlineStyle() and addInlineScript() register an inline asset:
<?php
// templates/cassiopeia_tallpine/index.php (excerpt)
$accent = $this->params->get('accentColour', '#2d6a4f');
// Validate before output: only a six digit hexadecimal colour may reach the page
if (!preg_match('/^#[0-9a-fA-F]{6}$/', $accent)) {
$accent = '#2d6a4f';
}
$wa->addInlineStyle(
':root { --tallpine-accent: ' . $accent . '; }',
['name' => 'tallpine.accent', 'position' => 'before'],
[],
['template.tallpine#style'] // placed relative to the last dependency
);
$wa->addInlineScript(
'document.documentElement.classList.add("js-ready");',
['name' => 'tallpine.js-ready'],
['type' => 'module'],
['template.tallpine']
);
Source: Joomla Manual: Web Asset Manager
The rules, all from the Manual and the renderer:
-
positionisbeforeorafter(the default). It places the inline content before or after the file asset named as the last dependency; with no dependency, after all other assets of that type. -
nameis optional. Without a name Joomla uses a hash of the content, which makes identical inline assets collapse into one; give a name when a later call should be able to refer to or override the asset. -
Inline assets can be dependencies of other inline assets. Using one as a dependency of a file asset works but its behaviour is documented as subject to change, so state a
positioninstead. -
A dependency that is not registered throws
UnsatisfiedDependencyException. -
attributesapply too, so['type' => 'module']makes an inline module. -
Inline assets in a
joomla.asset.jsonare possible but discouraged; use files.
Inline code is a security surface, so treat it as one. If the content includes any value that came from a request, a
database row or a parameter, it must be validated or encoded for its context, as in the colour check above. Where the
data is more than a fixed constant, do not build JavaScript with string concatenation: put the data in
addScriptOptions() and read it with Joomla.getOptions('com_tallpine.map'), and put translatable text in
Text::script(). When a Content Security Policy is active (see
Security hardening, MFA and passkeys), inline assets are the
first thing it blocks; Joomla adds the document’s CSP nonce to every script and style element it renders when one is
set, which is another reason to use the manager rather than printing <script> tags in a layout.
ES modules and import maps
A script asset declared with "type": "module" is a native ES module. Modules can import each other by bare names
(import {x} from 'tallpine-map'), and browsers resolve those names with an import map, a JSON block in the page
head. Joomla builds the import map for you from the registry.
Add "importmap": true to a script asset and Joomla includes it in the map. Two optional options refine it:
importmapName sets the name other modules import (the default is the asset name) and importmapScope limits the entry
to modules loaded from a path. This arrived with Joomla 5.0, together with the polyfill that makes import maps work in
older browsers; core itself uses it for the editor API (editor-decorator, editor-api) and the dialog
(joomla.dialog).
{
"name": "tallpine.map-core",
"type": "script",
"uri": "com_tallpine/map-core.js",
"attributes": {
"type": "module"
},
"importmap": true,
"importmapName": "@tallpine/map-core"
},
{
"name": "com_tallpine.trail-map",
"type": "script",
"uri": "com_tallpine/trail-map.js",
"attributes": {
"type": "module"
},
"dependencies": [
"core",
"tallpine.map-core"
]
}
// media/com_tallpine/js/trail-map.js
import { createMap } from '@tallpine/map-core';
const options = Joomla.getOptions('com_tallpine.map');
const container = document.getElementById('tallpine-map');
if (container && options) {
createMap(container, options);
}
Source: Joomla Manual: Web Asset Manager
The renderer emits the import map first, before any <script> element, as <script type="importmap"> with a JSON body
mapping each name to the asset’s URL (with its version query string), plus a scopes object for scoped entries. The map
must come before the first module that uses it, which is why Joomla renders it before the regular scripts. The same
version rules apply to its URLs. Because the browser only reads the first import map in a document, do not add your own
<script type="importmap"> to a layout: register an asset instead. An entry in the map does not load the file, it only
names it; the file loads when a module imports it, or when the asset is also used so that a <script> element is
emitted.
Web components
Joomla ships a loader for custom elements (joomla-alert, joomla-tab, joomla-field-media and so on). By convention
the asset is called webcomponent.<name>, is a module script plus a style, and is loaded asynchronously by the Web
Component loader instead of as an ordinary script. A fallback for browsers without module support lists a nomodule
script that depends on the wcpolyfill asset; Joomla 6.0 removed the polyfill asset, and the Behaviour - Backward
Compatibility 6 plugin keeps an empty placeholder with that name (and for the dropped chosen assets) so that an old
useScript('wcpolyfill') call does not throw. In PHP the pair is registered as a style plus a module script and then
used together.
<?php
// Registering a small custom element of the Tallpine template at run time
$wa->registerStyle('webcomponent.tallpine-weather', 'tallpine-weather.css')
->registerScript(
'webcomponent.tallpine-weather',
'tallpine-weather.js',
[],
['type' => 'module']
);
$wa->useStyle('webcomponent.tallpine-weather')
->useScript('webcomponent.tallpine-weather');
Source: Joomla Manual: Web Asset Manager
Overriding and disabling core assets
The cleanest way to change what Joomla loads is the registry: define an asset with the same type and name in a file that is read later. Because the child template’s file is read last, that is the natural place.
Replace a core asset
The club restyles the search tools of the administrator-independent front end and wants its own stylesheet used wherever the core one is requested:
{
"name": "searchtools",
"type": "style",
"uri": "tallpine-searchtools.css"
}
Source: Joomla Manual: Web Asset Manager
Placing this entry in templates/cassiopeia_tallpine/joomla.asset.json makes every extension that uses searchtools
load the template’s file (media/templates/site/cassiopeia_tallpine/css/tallpine-searchtools.css) instead of the core
one, because the template registry is loaded after media/system. The same can be done in code with
$wa->registerStyle('searchtools', 'tallpine-searchtools.css') before the asset is used. Redefining an asset keeps the
dependents working because they refer to it by name. It does not keep the old dependencies: the new entry replaces the
whole definition, so repeat any dependencies the original had.
Disable a core asset
disableStyle('name') only removes an asset that was activated directly. If something else depends on it, the manager
puts it back as a dependency. Cassiopeia’s stylesheet depends on fontawesome, so disableStyle('fontawesome') in a
child template does nothing visible. There are three honest ways out:
-
Redefine it as empty. Declare
fontawesomein the child’s file with an emptyuri(""): the asset still exists, so dependents are satisfied, but it has no file to output. Joomla uses this technique for its own dummy assets, and the Backward Compatibility 6 plugin ships such placeholders. -
Stop depending on it. Register a replacement for the parent’s style that omits the dependency, for example redefine
template.cassiopeia.ltrin the child with its owndependencieslist. -
Defer it. Set the
rel="lazy-stylesheet"attribute, as Cassiopeia does, when you only want it out of the critical path.
{
"name": "fontawesome",
"description": "Not used by the Tallpine template: icons come from the club's own SVG sprite.",
"type": "style",
"uri": ""
}
Source: Joomla Manual: Web Asset Manager
Before removing a core asset, check the front end and the administrator interface of every extension the site uses:
modules and component layouts that print Font Awesome classes show empty squares without it, and removing core breaks
the scripts that call Joomla.Text, Joomla.getOptions and the other core functions.
File-level and registry-level overrides compared
| Approach | What it changes | Use it when |
|---|---|---|
Redefine the asset in the template’s |
The asset’s URL, dependencies, attributes and version, for every user of the name |
You change how an asset loads or want your file used instead of the core one |
Place a file in the template’s media folder at the core file’s relative path |
The content of the file served, with the registry untouched |
You patch a single file (a core stylesheet) and want everything else, including dependencies, to stay as is |
Call |
One request, from the code that runs |
The decision depends on the page or the template parameters |
A third-party extension’s asset can be overridden the same way as a core one, which is why extension authors should use prefixed names. An override survives core updates but not changes in the asset’s name: after a major update, check that the names you override still exist (see Upgrading from Joomla 5 to 6).
Legacy methods and the move away from them
The Developer Manual lists Document::addScript(), Document::addStyleSheet() and the related addScriptDeclaration()
and addStyleDeclaration() calls as deprecated, with removal scheduled for Joomla 6, and the manager as the replacement.
In practice, treat every remaining call as technical debt: they bypass dependency resolution, have no version control
and are output in a different part of the renderer. The mapping is mechanical:
| Old code | Replace with |
|---|---|
|
Register the file in |
|
|
|
|
|
|
|
Keep: they are not deprecated |
The compatibility rules for the move to Joomla 6, including how the Backward Compatibility 6 plugin helps while you migrate, are in Upgrading from Joomla 5 to 6 and Deprecations and preparing for Joomla 7.
Custom asset classes
The default item class is Joomla\CMS\WebAsset\WebAssetItem. An asset can name another class with class (and
optionally namespace), which must implement WebAssetItemInterface or extend WebAssetItem. The namespace set at
the top of the file applies to every item, a namespace on an item overrides it, and with none given the default
namespace is Joomla\CMS\WebAsset; an explicitly empty namespace means the class name is used as it is. Joomla uses
this for core (CoreAssetItem), the keepalive script and Cassiopeia’s language-specific stylesheet
(LangActiveAssetItem). Two reasons to write one:
-
Choose a different file at run time. Override
getUri(), for example to returntallpine-de.csswhen the active language is German. CallresolvePath()from it when the path should be resolved the normal way. -
Run code when the asset is attached. Implement
WebAssetAttachBehaviorInterface; itsonAttachCallback(Document $doc)is called when the asset is attached to the document and can add script options. The asset must have been used before theonBeforeCompileHeadevent, otherwise the callback is ignored.
Debugging and testing
With Docker Compose running (see Local development with Docker),
two checks cover most problems. Switch on System > Global Configuration > Debug System, then look at the generated
head: assets carry data-asset-name and data-asset-dependencies.
# List every stylesheet and script the home page requests, with versions
curl -s http://localhost:8080/ | grep -oE '<(link|script)[^>]+(href|src)="[^"]+"'
# The import map, if any asset opted in
curl -s http://localhost:8080/ | grep -A12 'type="importmap"'
Source: Joomla Manual: Web Asset Manager
The Chrome or Firefox network panel then shows whether the child template’s files returned 200 or 404; a 404 for
media/templates/site/cassiopeia_tallpine/css/tallpine.css means the file is missing from the template’s media folder
or the <media> element of templateDetails.xml does not list its css folder. See
Debugging for the debug bar and the log.
Common errors and pitfalls
-
UnknownAssetException("Unknown asset"). The name was never registered, the registry file is not on the lookup list (a module that forgotaddRegistryFile()), or the type is wrong (a style called withuseScript). CheckassetExists(). -
UnsatisfiedDependencyException. An asset or preset lists a dependency that is not registered, or an inline asset named a missing one. -
RuntimeExceptionon every page. Ajoomla.asset.jsoncontains invalid JSON, or an entry has nonameor notype. -
The asset is registered but not output. It was never used, was used after the head was rendered, or was disabled while being a direct activation. Dependencies are enabled automatically; the asset itself is not.
-
The override has no effect. The child template is not the active template for that menu item (check the template style assignment), the name differs by a character or by type, or an extension registers it again later at run time.
-
disableScript()does not remove the script. Something depends on it. Use a registry override with an emptyuri. -
Everything is cached after an update. The
autoversion only changes with the media version; for your own files use a fixedversionstring and bump it with each release. -
The import map is ignored. A layout prints its own
<script type="importmap">, or the module that needs the name is loaded before the map. Let the renderer produce it. -
A script runs before the DOM exists. It is a classic script without
defer. Usetype: module, ordefer. -
The file does not load in debug mode only. Debug mode loads the unminified name; ship both the
.jsand the.min.jsfile. -
Joomla 3 habits. jQuery is not loaded for free. Declare
jqueryas a dependency or, better, stop using it.
Security considerations
-
Content Security Policy. The renderer adds the document’s nonce to script and style elements it generates. Inline assets work under a nonce-based policy; scripts printed by hand in a layout do not.
-
Subresource integrity. For a file from another host, set
integrityandcrossorigininattributesand pin a version in the URL. Prefer hosting libraries locally, which also avoids a third party seeing your visitors' requests; this is a privacy point for sites subject to data-protection law (see Privacy and action logs). -
Inline assets and user data. Anything that reaches
addInlineScript()oraddInlineStyle()is output without escaping. Validate or encode it for its context, and preferaddScriptOptions(), which encodes JSON safely. -
Registry files are code. A
joomla.asset.jsoncan name any class throughclass/namespace, andonAttachCallbackruns PHP on every request where the asset is used. Only install assets from sources you trust, and review extensions that override core assets (see Security in extensions). -
Cache keys. A stable version string lets a cache serve the same file to everybody;
nocacheand random query strings are a development aid and should never reach production.
Version notes
| Version | Change |
|---|---|
4.0 |
The Web Asset Manager, registry files and |
4.1 |
Child templates: the parent template’s registry file is read before the child’s. |
5.0 |
ES5 assets and their fallbacks are removed from core; ES module import maps ( |
5.4 (security fixes only) |
Legacy |
6.0 |
|
6.1 |
|
6.2 (current) |
No change to the asset format. |