(written by Grok and reviewed by Max Milbers)
Audience: Template builders, extension developers, shop owners who override one JS or CSS file
Status: VirtueMart 5
Resolver: vmJsApi::setPath in administrator/components/com_virtuemart/helpers/vmjsapi.php
Core files: media/com_virtuemart/js, media/com_virtuemart/css, media/com_virtuemart/images
1. Why the files moved
Joomla 4+ wants browser files (CSS, JavaScript, component images) under media/com_*. PHP stays in components/ and administrator/components/. Child templates already keep their own CSS and JS under media/templates/site/yourtemplate/. VirtueMart 5 follows the same split.
That is not a shop-security patch. It does not replace input filtering, tokens, or ACL. It does mean VirtueMart is ready for Joomla’s media layout, child templates, and a public folder that only serves static files.
The nice part for humans — and for anyone who likes a clean puzzle — is the same idea as a Linux overlay: same relative path, higher-priority folder wins. You copy one file, not the whole tree.
VirtueMart’s answer: core static files live in media/com_virtuemart/. A template overrides them by putting a file in the Joomla media-template folder. vmJsApi::setPath (used by addJScript, addvScriptModule, css) looks there first. You do not point assets_general_path at your template any more.
2. Core layout
No extra assets wrapper. js, css and images are siblings, so existing CSS url(../images/…) still works.
media/com_virtuemart/js/
media/com_virtuemart/css/
media/com_virtuemart/images/
Examples:
media/com_virtuemart/js/product/vmprices.module.jsmedia/com_virtuemart/js/vm-init-registry.module.jsmedia/com_virtuemart/css/vm-ltr-site.cssmedia/com_virtuemart/css/glightbox.cssmedia/com_virtuemart/images/icons/cart.svgmedia/com_virtuemart/images/vm-preloader.gif
Shop product photos (images/virtuemart/product/ and the media handler) are a different system. Leave them alone.
Admin chrome CSS for the vmadmin template stays under administrator/templates/vmadmin/. That is a template, not this component media tree. Leftover files under administrator/components/com_virtuemart/assets/ are a second tree (logos and old admin JS) and are not this article.
3. Canonical override (do this)
This is Joomla’s HTMLHelper rule for script('com_virtuemart/product/vmprices.module.js') with relative = true: type folder first (js / css / images), then the component name.
Site template cassiopeia, override the prices module:
media/templates/site/cassiopeia/js/com_virtuemart/product/vmprices.module.js
Same file as CSS:
media/templates/site/cassiopeia/css/com_virtuemart/vm-ltr-site.css
Same idea for an image used through setPath:
media/templates/site/cassiopeia/images/com_virtuemart/vm-preloader.gif
Child template mychild whose parent is cassiopeia: put the file on the child first. VirtueMart then tries the parent:
media/templates/site/mychild/js/com_virtuemart/product/vmprices.module.js
media/templates/site/cassiopeia/js/com_virtuemart/product/vmprices.module.js
Older templates that still keep CSS/JS inside templates/ (not inheritable / no media tree) can use:
templates/cassiopeia/js/com_virtuemart/product/vmprices.module.js
templates/cassiopeia/css/com_virtuemart/vm-ltr-site.css
If you also ship a .min.js / .min.css next to the source, setPath prefers the min file when debug is off (same as core). Edit the unminified file; do not hand-edit .min.js.
Do not set configuration assets_general_path to your template folder. That old trick replaced the whole tree. Folder overlay replaces one file. The config field stays for WordPress and emergencies. On Joomla the default is media/com_virtuemart/.
4. Lookup order in setPath
vmJsApi::setPath builds a list, reverses it, and takes the first file that exists (is_file). Minified candidates are tried first when minified output is on, then the unminified names.
Below is the check order for a site view, shop template cassiopeia, no child parent, file product/vmprices.module.js. First hit wins.
media/templates/site/cassiopeia/js/com_virtuemart/product/vmprices.module.js— Joomla standardtemplates/cassiopeia/js/com_virtuemart/product/vmprices.module.js— Joomla, old template foldermedia/templates/site/cassiopeia/html/com_virtuemart/assets/js/product/vmprices.module.js— VirtueMart layout-style folder, under mediatemplates/cassiopeia/html/com_virtuemart/assets/js/product/vmprices.module.js— VirtueMart layout-style folder, classictemplates/cassiopeia/js/product/vmprices.module.js— flat leftover (nocom_virtuemartsegment)media/com_virtuemart/js/product/vmprices.module.js— core
If the template is a child mychild with parent cassiopeia, this extra step runs immediately after (1):
media/templates/site/cassiopeia/js/com_virtuemart/product/vmprices.module.js
(the child path in (1) is then media/templates/site/mychild/js/com_virtuemart/product/vmprices.module.js).
CSS, same template, file vm-ltr-site.css:
media/templates/site/cassiopeia/css/com_virtuemart/vm-ltr-site.csstemplates/cassiopeia/css/com_virtuemart/vm-ltr-site.cssmedia/templates/site/cassiopeia/html/com_virtuemart/assets/css/vm-ltr-site.csstemplates/cassiopeia/html/com_virtuemart/assets/css/vm-ltr-site.csstemplates/cassiopeia/css/vm-ltr-site.css— flatmedia/com_virtuemart/css/vm-ltr-site.css— core
Administrator (VirtueMart backend template name vmadmin, file be/vmproducts.module.js):
media/templates/administrator/vmadmin/js/com_virtuemart/be/vmproducts.module.jsadministrator/templates/vmadmin/js/com_virtuemart/be/vmproducts.module.jsmedia/templates/administrator/vmadmin/html/com_virtuemart/assets/js/be/vmproducts.module.jsadministrator/templates/vmadmin/html/com_virtuemart/assets/js/be/vmproducts.module.jsadministrator/templates/vmadmin/js/be/vmproducts.module.js— flatmedia/com_virtuemart/js/be/vmproducts.module.js— core
The flat paths (no com_virtuemart after js/ or css/) are the old VirtueMart shortcut. They sit last among overrides, immediately before core. If you already placed the file on the Joomla standard path, is_file succeeds there and the flat folder is never checked. Please move overrides to the standard path. The flat fallback is compatibility, not the public API.
If a caller passes an extra directory into setPath (unusual for templates), that directory is tried after the layout-style folders and before the flat path.
5. ES modules: import map, not ../
Static import cannot read window.Virtuemart. A relative import is resolved against the file that was loaded. If you override only product/vmprices.module.js, then:
import vmInitRegistry from '../vm-init-registry.module.js';
looks next to the override, not next to core, and breaks.
VirtueMart registers an import map prefix and core modules write:
import vmInitRegistry from 'virtuemart/vm-init-registry.module.js';
import { startVmLoading, stopVmLoading } from 'virtuemart/ajax/vmloading.module.js';
The map key virtuemart/ points at media/com_virtuemart/js/ (including the Joomla subdirectory, for example /joomla5/media/com_virtuemart/js/).
If you override a module on the canonical Joomla path, PHP adds an extra map entry for that filename so other files that import virtuemart/product/vmprices.module.js get your copy. The scanner looks in:
media/templates/site/cassiopeia/js/com_virtuemart/templates/cassiopeia/js/com_virtuemart/templates/cassiopeia/html/com_virtuemart/assets/js/media/templates/site/cassiopeia/html/com_virtuemart/assets/js/- and the parent’s
media/templates/site/cassiopeia/js/com_virtuemart/when you use a child template
It does not scan the flat folder templates/cassiopeia/js/. Another reason to use the Joomla path.
Keep relative imports only inside a third-party library that always ships as one folder (GLightbox internals, Alpine’s own file, Sortable’s ESM build).
6. Runtime paths on window.Virtuemart
PHP publishes these in vmJsApi::vmVariables() (root includes the Joomla subdirectory):
| Property | Typical value | Use for |
|---|---|---|
Virtuemart.assetsPath |
/media/com_virtuemart/ |
Parent of js, css, images |
Virtuemart.jsPath |
/media/com_virtuemart/js/ |
Dynamic script URLs, lazy loads |
Virtuemart.cssPath |
/media/com_virtuemart/css/ |
Constructed stylesheet URLs |
Virtuemart.imagesPath |
/media/com_virtuemart/images/ |
Fetch SVG icons, constructed image URLs |
These are the runtime layer. They do not replace static import. Icon loading uses Virtuemart.imagesPath plus icons/, then the shop-template icon folders (see the SVG single-shim article).
7. Quick start
Override the frontend prices module in template cassiopeia:
- Copy
media/com_virtuemart/js/product/vmprices.module.jstomedia/templates/site/cassiopeia/js/com_virtuemart/product/vmprices.module.js. - Keep the imports as
virtuemart/…. Do not change them to../. - If you ship minified output, also place
vmprices.module.min.jsin that same override folder. - Reload with browser cache disabled. Network panel: the script URL should be under
media/templates/site/cassiopeia/.
Override one stylesheet:
- Copy
media/com_virtuemart/css/vm-ltr-site.csstomedia/templates/site/cassiopeia/css/com_virtuemart/vm-ltr-site.css. - Remember
url(../images/…)in that file is relative to the CSS file. If you override CSS only, those images resolve under the template tree, not undermedia/com_virtuemart/images/. Prefer extra CSS in the template, or copy the images you still reference.
8. assets_general_path
Configuration default on Joomla: media/com_virtuemart/ (parent of js, css, images).
vmJsApi::getAssetsGeneralPath() adds a trailing slash. If an updated shop still stores the old value components/com_virtuemart/assets/, that string is remapped to media/com_virtuemart/. That is not a second search in the old folder. Custom values (for example a WordPress plugin path) are left alone.
On live Joomla shops the old directory components/com_virtuemart/assets/ may still exist after an update (custom scripts, leftover VirtueMart 4 files). VirtueMart does not delete it automatically. A tools cleanup button is the opt-in way to remove leftovers. Do not put new overrides there.
9. Do’s and don’ts
- Do override on
media/templates/site/yourtemplate/js/com_virtuemart/(andcss,images). - Do keep
import … from 'virtuemart/…'inside overridden modules. - Do use
Virtuemart.jsPath/Virtuemart.imagesPathfor URLs you build at runtime. - Do not point
assets_general_pathat the template “because that is how we did it in VirtueMart 3”. - Do not drop files in
media/com_virtuemart/to override core — that is the core tree; updates overwrite it. - Do not use the flat path
templates/cassiopeia/js/vmprices.module.jsfor new work. - Do not expect a relative
importfrom an overridden file to find core siblings. - Do not treat this move as a security fix in shop marketing.
- Do not hand-edit
.min.jsor.min.css.
10. FAQ
Why not copy the whole assets folder into media/com_virtuemart/assets?
An extra assets wrapper is not Joomla. With js and css and images as siblings, relative CSS URLs stay valid.
I overrode one CSS file and the background images vanished.url(../images/foo.png) is relative to the CSS file. Copy the images or add a small extra stylesheet in the template instead of replacing the whole core CSS file.
Does a missing override folder cause extra HTTP 404s?
No. setPath uses is_file on disk. Only the winning path is printed as a URL.
WordPress?
There is no Joomla media-template overlay. Keep assets_general_path pointed at where the plugin puts the files. The same Virtuemart.jsPath object still works.
11. Files
- Resolver and import map:
administrator/components/com_virtuemart/helpers/vmjsapi.php(setPath,getAssetsGeneralPath,emitImportMap,vmVariables) - Core JS / CSS / images:
media/com_virtuemart/js,media/com_virtuemart/css,media/com_virtuemart/images - Default config:
assets_general_path=media/com_virtuemart/inadministrator/components/com_virtuemart/virtuemart_defaults.cfg-dist - Load API:
vmJsApi::addJScript,vmJsApi::addvScriptModule,vmJsApi::css
Related Technics: SVG icon single-shim (template icon drop folders), VMInit registry (how modules register), vmJsApi dual-load pattern.