Templates in Joomla
The template is the part of Joomla that most people think of as "the design". In reality it is more than that. The template is the page frame: it decides where the component lands, where the modules go, what the HTML looks like, and how the whole site feels.
How Joomla turns component output and modules into a finished HTML page.
This article explains how Joomla templates really work. It starts with the basics for website owners and editors, moves on to the practical setup for administrators, and ends with the technical details for developers. You will learn what a template is, how it differs from a style, how it renders a page, how to override output safely, and how to avoid the most common mistakes.
The one idea to take home is this: a template is not a design, it is a PHP layout that renders a design. It answers a single question on every request: given this component output and these modules, what HTML do we send to the browser? Learn how one template is assembled, and you can theme, override, and build any Joomla site.
1. The Basics
1.1 Where Templates Sit in the Extension Family
A template is one of Joomla's five extension types. Each type has its own job and its own naming prefix:
| 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 (defines the positions) | 1 active style | tpl_ |
| Language | Translations | 1 active | - |
The component and the modules do not decide where they appear on the page. The template decides that, by placing position slots that the modules drop into.
1.2 What a Template Actually Is
It helps to picture the page as a frame with slots in it:
┌───────────────────────────────────────────┐
│ TEMPLATE (index.php - the page frame) │
│ ┌──────────────────────────────────────┐ │
│ │ position: menu [MODULE] │ │
│ ├─────────┬──────────────────┬─────────┤ │
│ │ sidebar │ COMPONENT │ sidebar │ │
│ │ -left │ (main page) │ -right │ │
│ ├─────────┴──────────────────┴─────────┤ │
│ │ position: footer [MODULE] │ │
│ └──────────────────────────────────────┘ │
└───────────────────────────────────────────┘
If the page were a framed painting: the component is the painting, the modules are notes pinned around it, and the template is the frame, the wall, and the lighting.
1.3 Two Templates Are Always Active
Every Joomla site runs two independent templates at the same time, one for each side of the site:
| Client | Default template | Lives in |
|---|---|---|
| Site (front-end, visitors) | Cassiopeia | templates/cassiopeia/ |
| Administrator (back-end) | Atum | administrator/templates/atum/ |
public_html/
├── templates/ (SITE templates)
│ ├── cassiopeia/
│ └── system/ (fallback: error pages, offline)
└── administrator/
└── templates/ (ADMIN templates)
├── atum/
└── system/
This separation is useful: you can change the front-end's look without touching the admin, and the other way round.
1.4 Template vs. Style: the Crucial Distinction
This single point trips up almost everyone. A template and a style are two different things:
| Template | Style | |
|---|---|---|
| What it is | The installed files (PHP, CSS, XML) | A saved set of parameter values for a template |
| How many | One per install | Many per template |
| Stored in | #__extensions + the filesystem |
#__template_styles |
| Example | "Cassiopeia" | "Cassiopeia - Blue", "Cassiopeia - Red homepage" |
Cassiopeia (template)
├── Style: "Default" blue brand, sticky header
├── Style: "Campaign 2026" red brand, no header
└── Style: "Landing pages" minimal, no navigation
In the admin, System → Templates → Site Templates lists the files, while System → Templates → Site Template Styles lists the styles. You assign styles to menu items, never templates.
2. Using Templates Without Code
2.1 The Template Manager
Open System → Templates. For each client (site and administrator) you get two lists:
- Styles: create or duplicate styles, set the Default, assign styles to menu items, and edit parameters.
- Templates: see the installed template files, edit them in the built-in code editor, and manage child templates and overrides.
2.2 The Default Style and Per-Menu Assignment
Exactly one style is the Default. Joomla uses it for any page that has no more specific assignment. Any other style can be assigned to specific menu items, which overrides the default on those pages only.
Menu item "Home" → Style: "Campaign 2026" (red)
Menu item "Blog" → (no assignment) → Default style (blue)
Menu item "Contact us" → Style: "Landing pages" (minimal)
This is how one site shows different layouts on different pages without installing a second template.
2.3 Template Parameters (Options)
Each style exposes parameters that the template author defined. Cassiopeia, for example, offers options such as:
- Brand on or off, logo, title, and tagline.
- Colour scheme or theme (light, dark, custom).
- Fluid or fixed container width.
- Sticky header and a back-to-top button.
- Which font scheme to load.
These are simply form fields declared in templateDetails.xml and saved as JSON in the params column of #__template_styles. Two styles of the same template differ only in those saved values.
3. Inside a Template (the Anatomy)
3.1 The Folder Layout
A modern Joomla template (versions 4, 5, and 6) has a clear structure:
templates/cassiopeia/
├── templateDetails.xml manifest: name, positions, params, files
├── index.php THE PAGE FRAME (main layout)
├── error.php layout for 403 / 404 / 500 pages
├── offline.php layout when the site is offline
├── component.php bare layout for tmpl=component (print/popups)
├── joomla.asset.json declares the template's CSS/JS assets
└── html/ OVERRIDES of component and module output
├── layouts/
│ └── chromes/ module "chrome" (card.php, noCard.php)
├── mod_menu/
└── com_content/
A big change since Joomla 4: the CSS, JS, SCSS, and images moved out of the template folder. The template folder now holds mostly PHP logic.
3.2 Where the Media Lives Now
The actual stylesheets and scripts are no longer inside templates/. They live under /media/:
media/templates/
├── site/
│ └── cassiopeia/
│ ├── css/ template.min.css, template-rtl.min.css
│ ├── js/
│ ├── scss/ source SCSS, compiled to css/
│ └── images/
└── administrator/
└── atum/
So the split is:
templates/cassiopeia/holds the logic (PHP layouts and the manifest).media/templates/site/cassiopeia/holds the assets (compiled CSS, JS, images).
The line <media destination="templates/site/cassiopeia" folder="media"> in the manifest is what installs files into /media/....
3.3 The Four PHP Layout Files
A template can provide up to four layout files. Joomla picks the right one depending on the situation:
| File | Rendered when |
|---|---|
index.php |
Every normal page - the full frame. |
component.php |
?tmpl=component - content only, no frame (print, modals, popups). |
error.php |
An HTTP error (404, 403, 500) is thrown. |
offline.php |
The site is set offline in Global Configuration. |
Each one is a normal PHP file with a full <html>...</html> document, sprinkled with jdoc tags that Joomla replaces with real content.
4. How a Template Renders a Page
4.1 jdoc:include - the Magic Placeholders
The template is mostly static HTML with special <jdoc:include> tags. During rendering, Joomla swaps each tag for generated content:
<head>
<jdoc:include type="metas" /> <!-- meta tags, base href -->
<jdoc:include type="styles" /> <!-- all CSS the page needs -->
<jdoc:include type="scripts" /> <!-- all JS the page needs -->
</head>
<body>
<jdoc:include type="modules" name="menu" style="none" />
<jdoc:include type="message" /> <!-- system "Saved!" / error notices -->
<jdoc:include type="component" /> <!-- THE COMPONENT OUTPUT (once!) -->
<jdoc:include type="modules" name="sidebar-right" style="card" />
</body>
type= | Replaced with |
|---|---|
metas / styles / scripts |
The <head> contents (split into three since Joomla 4). |
component |
The current component's HTML - exactly once. |
modules |
All modules published to a named position. |
module |
A single module by name. |
message |
Queued system messages (success, warning, error). |
4.2 Module Positions
A position is just a named slot. The template declares its positions in templateDetails.xml:
<positions>
<position>menu</position>
<position>sidebar-left</position>
<position>sidebar-right</position>
<position>footer</position>
</positions>
It then fills those positions in index.php with <jdoc:include type="modules" name="...">. Cassiopeia ships around 18 positions (topbar, menu, banner, top-a/b, sidebar-left/right, main-top/bottom, bottom-a/b, footer, and more).
A module assigned to a position that the template never renders simply does not show. The position must exist and be output by a jdoc tag.
4.3 Module "Chrome" (the Wrapper Around Each Module)
The style="..." attribute on a modules tag picks the chrome: the HTML wrapper drawn around each module (its title, card, and div classes). Cassiopeia defines its own chrome in html/layouts/chromes/:
html/layouts/chromes/
├── card.php wraps the module in a styled "card"
└── noCard.php
style= | Effect |
|---|---|
none |
No wrapper - raw module output. |
card |
Bootstrap-style card with a heading. |
| (custom) | Any chrome layout you add yourself. |
Chrome moved from the old modChrome_*() functions to clean layout files in Joomla 4, which makes it easy to override or extend.
4.4 The Full Render Flow
Putting it together, here is what happens on every request:
Component + modules produce their HTML
│
v
Template index.php is loaded (the frame)
│
v
Document renderer walks the <jdoc:include> tags
- type="component" → drop in the component HTML
- type="modules" → render every module in that position (with chrome)
- type="styles" → inject CSS gathered by the WebAssetManager
│
v
Final HTML document → sent to the browser
Back to top5. The Manifest and the Database
5.1 templateDetails.xml - the Manifest
The manifest declares the template to Joomla: its files, positions, parameters, languages, and (for child templates) its parent.
<extension type="template" client="site">
<name>cassiopeia</name>
<version>1.0</version>
<inheritable>1</inheritable> <!-- can be a parent template -->
<files>
<filename>index.php</filename>
<filename>component.php</filename>
<folder>html</folder>
</files>
<media destination="templates/site/cassiopeia" folder="media">
<folder>css</folder><folder>scss</folder><folder>images</folder>
</media>
<positions> ... </positions> <!-- the slots the template offers -->
<config>
<fields name="params"> <!-- the style parameters (Options) -->
<fieldset name="advanced"> ... </fieldset>
</fields>
</config>
</extension>
5.2 Where Templates Are Recorded
Three database tables describe the template world:
| Table | Holds |
|---|---|
#__extensions |
One row per installed template (type='template', element='cassiopeia', client_id 0=site / 1=admin, enabled, and a manifest_cache JSON snapshot of the XML). |
#__template_styles |
One row per style: template, client_id, home (the default flag), title, and params (JSON of the Options). |
#__menu |
Each menu item's template_style_id column points to the style assigned to that page (0 = use the default). |
SELECT id, template, title, home
FROM #__template_styles
WHERE client_id = 0;
-- home='1' → the default style for the default language
-- home='0' → a non-default style (may still be assigned per menu item)
-- which style each menu item uses:
SELECT title, template_style_id
FROM #__menu
WHERE template_style_id <> 0;
So the default lives in #__template_styles.home, while a per-page override is just a template_style_id on the menu item. The style's parameters always live in #__template_styles.params.
6. Template Overrides (the Superpower)
6.1 Why Overrides Exist
Never edit a component's or module's core files. Joomla overwrites them on the next update, and your changes are lost. Instead, the template can shadow any layout by placing a copy in its html/ folder.
Copy: components/com_content/tmpl/article/default.php
To: templates/cassiopeia/html/com_content/article/default.php
Joomla checks the template's html/ folder first. If a matching file exists there, that file wins.
6.2 What You Can Override
| Target | Override path under html/ |
|---|---|
| A component view/layout | html/com_content/article/default.php |
| A module | html/mod_menu/default.php |
| A shared JLayout | html/layouts/joomla/form/field/... |
| Module chrome | html/layouts/chromes/card.php |
| Pagination, form fields, system layouts | html/layouts/... |
The admin even has a built-in tool. Open System → Templates → (your template) → Create Overrides and Joomla copies the correct file into html/ for you.
6.3 What You Get Inside an Override File
An override is a layout, so it runs in the View's context. The data the View prepared is already available on $this:
<?php defined('_JEXEC') or die; ?>
<h1><?php echo $this->escape($this->item->title); ?></h1>
<?php foreach ($this->items as $item) : ?>
<article><?php echo $item->introtext; ?></article>
<?php endforeach; ?>
<?php echo $this->pagination->getListFooter(); ?>
| Variable | Typically holds |
|---|---|
$this->item |
The single record (article, contact) on a detail view. |
$this->items |
The list of records on a list or category view. |
$this->pagination |
The pager object for list views. |
$this->params |
Merged menu-item and component parameters. |
$this->escape() |
Shortcut for safe output (see section 11). |
You only change HTML, never queries. The Model already fetched everything, so do not add database calls inside a layout.
6.4 Alternative Layouts (do not Override - Add)
A cleaner option than overriding the default layout is to ship an extra layout and pick it per menu item or module:
html/com_content/category/
├── blog.php
└── blog_grid.php your new "Grid" layout
The new layout appears in the menu item's Choose a Layout dropdown. The original stays untouched and fully update-safe.
Back to top7. The Web Asset Manager
7.1 joomla.asset.json - Declare, Do Not Hardcode
Since Joomla 4, templates and extensions declare CSS and JS declaratively, with names, versions, and dependencies, instead of hand-writing <link> and <script> tags:
{
"name": "cassiopeia",
"assets": [
{
"name": "template.cassiopeia.ltr",
"type": "style",
"uri": "template.min.css",
"dependencies": [ "fontawesome" ]
}
]
}
7.2 Using Assets from PHP
$wa = $this->getWebAssetManager();
$wa->useStyle('template.cassiopeia.ltr') // resolves dependencies for you
->useScript('template.cassiopeia');
// Register and use an ad-hoc file (as the child template does):
$wa->registerAndUseStyle('colors_custom', 'global/colors.css');
Why this is better than manual tags:
- Dependency resolution: ask for the template CSS, and Font Awesome comes along too.
- De-duplication: the same library is never loaded twice.
- Versioning and ordering are handled centrally, then output through
<jdoc:include type="styles" />.
In short, the WebAssetManager is what fills the styles and scripts jdoc placeholders.
8. Child Templates (Inheritance)
8.1 The Idea
Since Joomla 4.1, you no longer need to fork Cassiopeia and lose every future update. You can create a child template that inherits everything from a parent and contains only your differences.
templates/
├── cassiopeia/ parent, <inheritable>1</inheritable>
└── cassiopeia_extended/ child, <parent>cassiopeia</parent>
├── index.php require parent, then add a few tweaks
└── html/ only the overrides you change
8.2 What a Child Looks Like
The child's index.php can simply delegate to the parent and layer changes on top:
// templates/cassiopeia_extended/index.php
require JPATH_THEMES . '/cassiopeia/index.php'; // reuse the whole frame
$wa = $this->getWebAssetManager();
$wa->registerAndUseStyle('colors_custom', 'global/colors.css'); // just override colours
And the manifest declares the relationship:
<extension type="template" client="site">
<name>cassiopeia_extended</name>
<inheritable>0</inheritable>
<parent>cassiopeia</parent>
</extension>
The payoff is real: when the parent gets a security update, your child inherits it automatically. You only maintain the difference.
Back to top9. Template Frameworks (the Real World)
9.1 Most Sites Do Not Use Raw Cassiopeia
In practice, a large share of Joomla sites are built on a template framework: a parent system that adds a visual builder and reusable parts on top of Joomla's template layer.
| Framework | Approach | Notable features |
|---|---|---|
| Helix Ultimate | Free, widely bundled with commercial templates | Drag-and-drop layout builder, mega menu, presets |
| Gantry 5 | Config-driven | YAML configuration, Twig templating, particles |
| YOOtheme Pro | Commercial | Visual page builder, dynamic content from Joomla data |
The trade-off: frameworks speed up design work and empower non-coders, but they add a layer of abstraction (and sometimes performance overhead) on top of the native template you have just learned.
Everything in this article still applies underneath. Frameworks are all rendered through index.php, jdoc tags, positions, and overrides. The framework simply generates that layer for you.
10. Accessibility and Performance
10.1 Accessibility Is a First-class Joomla Goal
Cassiopeia and Atum were rebuilt around accessibility. A good template should deliver:
- WCAG 2.1 compliance out of the box.
- Full keyboard navigation and visible focus states.
- Screen-reader support through correct semantics and ARIA attributes.
- Sufficient colour contrast and resizable text.
Cassiopeia ships as a strong accessibility baseline. Overriding its markup carelessly is the easiest way to break that, so preserve roles and ARIA attributes when you edit layouts.
10.2 Templates Own the Core Web Vitals
The template controls the <head>, the asset loading, and the image markup, so it largely decides your Core Web Vitals:
| Metric | What the template influences |
|---|---|
| LCP (Largest Contentful Paint) | Hero image and CSS delivery, render-blocking resources. |
| CLS (Cumulative Layout Shift) | Image dimensions, font-loading strategy. |
| INP (interactivity) | How much JavaScript is loaded, and when. |
Optimisation techniques that live in the template:
- Minify CSS and JS (Cassiopeia ships
*.min.css) and let the WebAssetManager de-duplicate. - Defer non-critical JavaScript.
- Lazy-load below-the-fold images with
loading="lazy". - Serve WebP or AVIF and set explicit width and height.
- Avoid heavy framework overhead you do not use.
11. Security and Template Events
11.1 Escape Everything You Output
Override files print raw data, so they are a classic injection point. Always escape text before output:
echo $this->escape($title); // View helper (recommended)
echo htmlspecialchars($title, ENT_QUOTES, 'UTF-8'); // plain PHP equivalent
Secure-template habits:
- Escape output, especially anything supplied by a user.
- Use no inline JavaScript. Register scripts through the WebAssetManager (this also helps a Content Security Policy).
- Use Joomla APIs instead of hand-rolled HTML or SQL.
- Keep logic out of layouts (no database calls - see section 6.3).
11.2 Events That Touch the Template
Plugins can change the rendered page around the template through document and application events, without editing any template file:
| Event | Fires | Typical use |
|---|---|---|
onBeforeCompileHead |
Just before the <head> is assembled |
Inject or strip CSS and JS, add meta tags. |
onAfterRender |
After the full HTML is built | Post-process the final output string. |
// A system plugin adding a stylesheet to every page:
public function onBeforeCompileHead(): void
{
$this->getApplication()->getDocument()
->getWebAssetManager()
->registerAndUseStyle('my.extra', 'media/mything/extra.css');
}
This is loose coupling again: the template renders the frame, and plugins adjust the assets and output through events. Neither one edits the other.
Back to top12. Building Your Own Template
12.1 The Minimum Viable Template
A working site template needs surprisingly few files:
tpl_hello/
├── templateDetails.xml name, files, positions, params
├── index.php <html> + jdoc tags
├── error.php
└── component.php
A barely-working index.php looks like this:
<?php defined('_JEXEC') or die; ?>
<!DOCTYPE html>
<html>
<head>
<jdoc:include type="metas" />
<jdoc:include type="styles" />
<jdoc:include type="scripts" />
</head>
<body>
<jdoc:include type="modules" name="menu" style="none" />
<jdoc:include type="message" />
<jdoc:include type="component" /> <!-- required: the page content -->
</body>
</html>
Then:
- Zip it and install through
Extensions → Install. - Open
System → Templatesand set its style as the Default. - Reload the site, and your frame renders.
In practice, start from a child of Cassiopeia rather than from scratch. You inherit positions, assets, and accessibility for free.
Back to top13. Common Mistakes and Pitfalls
- Editing core files instead of using
html/overrides. Your changes are lost on the next update. - Forgetting
<jdoc:include type="component" />. The result is a page with no content. - Confusing template with style. The template is the files; the style is the saved parameters.
- Assigning a module to a position the template never outputs. The module silently vanishes.
- Hardcoding
<link>and<script>tags instead of using the WebAssetManager. You get duplicate, mis-ordered assets. - Forking Cassiopeia when a child template would have kept you update-safe.
- Breaking accessibility by removing ARIA roles or semantic tags while restyling.
- Adding database queries inside a layout. The Model already prepared the data on
$this.
14. Best Practices
If you only remember a few things from this article, remember these:
- Treat the template as a layout, not a design. The design lives in CSS under
/media/. - Use styles and per-menu assignment for different looks, instead of installing extra templates.
- Override output in
html/, and prefer alternative layouts over replacing the default. - Declare assets in
joomla.asset.jsonand let the WebAssetManager handle order and dependencies. - Build on a child template so you keep inheriting parent updates.
- Preserve semantics and ARIA, and keep an eye on Core Web Vitals.
15. Quick Reference
FRAME templates/{tpl}/index.php (the jdoc layout)
ASSETS media/templates/site/{tpl}/ (css, js, scss, images)
STYLE System → Templates → Site Template Styles
DEFAULT Set the Default style (home=1)
PER PAGE Assign a style to a menu item (template_style_id)
PLACEHOLDER <jdoc:include type="component|modules|styles|message" />
POSITION Declare in XML, fill with jdoc; reveal live with ?tp=1
NO FRAME Append ?tmpl=component to a URL
OVERRIDE templates/{tpl}/html/com_xxx/view/layout.php
CHILD <parent>cassiopeia</parent> in the manifest
ESCAPE echo $this->escape($value);
EVENTS onBeforeCompileHead, onAfterRender (plugins)
Back to top16. Summary
In Joomla, the template is not just "the design". It is the engine that turns component output and modules into a finished HTML page. It touches almost every layer of the site:
- Layout: where the component and modules land.
- Branding: through styles and parameters, without code.
- Output: through update-safe overrides in
html/. - Performance: through the WebAssetManager and the Core Web Vitals.
- Accessibility: through correct semantics and ARIA.
- Maintainability: through child templates that inherit updates.
Once you understand one template's anatomy - the manifest, index.php, positions, jdoc tags, and overrides - you can theme, override, and build any Joomla site. Frameworks like Helix, Gantry, and YOOtheme only generate this same layer for you; the foundation underneath is always the same.
If you are planning a new Joomla site, migrating from an older version, or you are stuck with a template that does not render the way you expect, it pays to understand this architecture early. Templates are simple to use, but the details underneath are where solid, fast, and accessible Joomla sites are made.
Back to top

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


