Skip to main content

Templates in Joomla

05 June 2026

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:

TypeRolePer pagePrefix
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:

ClientDefault templateLives 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:

 TemplateStyle
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.

Back to top

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.

Back to top

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:

FileRendered 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.

Back to top

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 top

5. 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:

TableHolds
#__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.

Back to top

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

TargetOverride 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(); ?>
VariableTypically 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 top

7. 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.

Back to top

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 top

9. 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.

FrameworkApproachNotable 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.

Back to top

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:

MetricWhat 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.
Back to top

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:

EventFiresTypical 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 top

12. 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:

  1. Zip it and install through Extensions → Install.
  2. Open System → Templates and set its style as the Default.
  3. 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 top

13. 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.
Back to top

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.json and 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.
Back to top

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 top

16. 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
Templates in Joomla
Peter Martin

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