Joomla component development MVC architecture diagram

Joomla Component Development: Step-by-Step Guide

Marco Vasquez
Written By Marco Vasquez
Marcus Chen
Reviewed By Marcus Chen
Last Updated April 19, 2026

What Is a Joomla Component?

When we start a new Joomla project we often wonder whether a component, a module, or a plugin is the right tool. In Joomla’s architecture a component is the primary container for the main content of a site. It handles the full request‑response cycle, from routing to data storage and presentation. Modules and plugins, by contrast, are auxiliary pieces that modify or supplement the output of a component.

Components follow the Model‑View‑Controller (MVC) pattern, which keeps business logic, data handling, and the user interface separate. This separation makes our code easier to test, extend, and maintain. The MVC approach is built into Joomla 4 and 5, so every component we create automatically benefits from the framework’s routing, language handling, and security layers.

We usually decide to build a custom component when the existing extensions do not provide the exact data structures or workflows we need. If the requirement is limited to a small piece of content that appears in a fixed position, a module might be sufficient. For event‑driven tasks such as content filtering or authentication, a plugin is the appropriate choice. If you are new to the platform, our guide on what Joomla is covers the fundamentals.

FeatureComponentModulePlugin
PurposeFull‑page application, handles routing and CRUDSide‑box or position‑based outputEvent listener, modifies behavior
Entry pointindex.php?option=com_…Rendered in template positionsTriggered by Joomla events
Structureadmin + site folders, MVC classesSingle folder, layout filesSingle folder, event handlers
Typical use caseComplex data management (e.g., a booking system)Banner, login box, custom HTMLSEO meta‑tags, content filters

For a deeper look at the MVC pattern in Joomla, the official Joomla MVC documentation provides a solid foundation. The Joomla component development manual also walks through the lifecycle of a component request.

Setting Up Your Joomla Development Environment

Before we write any code we need a reliable local environment. Most of us use XAMPP or Docker to spin up a LAMP stack that matches the production server. After installing Joomla 4 or 5, we point the web root to the htdocs (or Docker volume) and create a database for the site.

Our file structure looks like this:

joomla/
 ├─ administrator/
 │   └─ components/
 │       └─ com_example/      ← admin side of the component
 ├─ components/
 │   └─ com_example/          ← site side of the component
 ├─ templates/
 │   └─ cassiopeia/
 └─ configuration.php

We recommend using an IDE that understands PHP namespaces, such as PhpStorm or VS Code with the PHP Intelephense extension. Xdebug integration lets us set breakpoints inside model or controller files, which speeds up troubleshooting.

When the environment is ready, we can access the Joomla admin panel to install our component later. The install Joomla extensions guide explains the upload process in detail.

Understanding the MVC Architecture in Joomla

Joomla’s MVC implementation relies on PHP namespaces and the service container introduced in Joomla 4. Each part of the MVC trio lives in a dedicated folder and follows a naming convention that the autoloader can resolve automatically.

Model – The model contains all database interactions. We typically extend Joomla\CMS\MVC\Model\BaseDatabaseModel and use the DatabaseDriver object for queries. The model returns plain PHP objects or arrays that the view can consume.

View – The view prepares data for output. It receives the model’s result, assigns it to the template, and loads a layout file from tmpl. Views inherit from Joomla\CMS\MVC\View\HtmlView and can override methods such as display() to add custom logic.

Controller – The controller maps HTTP requests to model actions. Joomla routes the request to a controller class that extends Joomla\CMS\MVC\Controller\BaseController. Inside the controller we can call $model->save($data), $model->delete($id), and then redirect to the appropriate view.

All three layers share the same namespace, for example Example\Component\Example\Site for the front‑end and Example\Component\Example\Administrator for the back‑end. This namespace structure is reflected in the folder hierarchy:

src/
 ├─ Controller/
 │   ├─ DisplayController.php
 │   └─ ItemController.php
 ├─ Model/
 │   ├─ ItemsModel.php
 │   └─ ItemModel.php
 └─ View/
     ├─ Items/
     │   ├─ tmpl/
     │   │   └─ default.php
     │   └─ HtmlView.php
     └─ Item/
         ├─ tmpl/
         │   └─ edit.php
         └─ HtmlView.php

Because Joomla 4/5 uses Composer‑style autoloading, we only need to declare the namespace in the component’s services provider. The framework automatically registers the service provider, which lets us inject the model into the controller without manual includes.

Creating Your First Joomla Component

Let us walk through the skeleton of a new component called com_example. We start with the manifest XML file, which tells Joomla how to install the extension and where to find its files.

<?xml version="1.0" encoding="utf-8"?>
<extension type="component" version="4.0" method="upgrade">
    <name>com_example</name>
    <author>Your Name</author>
    <version>1.0.0</version>
    <description>A simple example component</description>
    <namespace path="src">Example\Component\Example</namespace>

    <files folder="site">
        <folder>src</folder>
        <folder>tmpl</folder>
    </files>

    <administration>
        <menu>Example</menu>
        <files folder="admin">
            <folder>forms</folder>
            <folder>services</folder>
            <folder>sql</folder>
            <folder>src</folder>
            <folder>tmpl</folder>
        </files>
    </administration>
</extension>

Next we create the basic folder layout. The admin folder holds the back‑end MVC files, while the site folder contains the front‑end equivalents. Both sides have a src directory where we place the namespaced PHP classes.

com_example/
 ├─ admin/
 │   ├─ forms/
 │   ├─ services/
 │   │   └─ provider.php
 │   ├─ sql/
 │   │   ├─ install.mysql.utf8.sql
 │   │   └─ uninstall.mysql.utf8.sql
 │   ├─ src/
 │   │   ├─ Controller/
 │   │   ├─ Model/
 │   │   ├─ Table/
 │   │   └─ View/
 │   └─ tmpl/
 ├─ site/
 │   ├─ src/
 │   │   ├─ Controller/
 │   │   ├─ Model/
 │   │   └─ View/
 │   └─ tmpl/
 └─ example.xml

We add a service provider to register our classes with Joomla’s dependency injection container. The provider lives in admin/services/provider.php and is the entry point for the framework.

<?php
defined('_JEXEC') or die;

use Joomla\CMS\Dispatcher\ComponentDispatcherFactoryInterface;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\CMS\Extension\Service\Provider\ComponentDispatcherFactory;
use Joomla\CMS\Extension\Service\Provider\MVCFactory;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;

return new class implements ServiceProviderInterface
{
    public function register(Container $container)
    {
        $container->registerServiceProvider(new MVCFactory('\\Example\\Component\\Example'));
        $container->registerServiceProvider(new ComponentDispatcherFactory('\\Example\\Component\\Example'));

        $container->set(
            ComponentInterface::class,
            function (Container $container) {
                $component = new \Joomla\CMS\Extension\MVCComponent();
                $component->setMVCFactory($container->get(MVCFactoryInterface::class));
                return $component;
            }
        );
    }
};

With the manifest, folder structure, and service provider in place, we can zip the component and install it through the Joomla admin panel. The sections below flesh out the model, view, and controller files, and we will show how to create a database table and build both admin and frontend interfaces.

Building the Database Layer

Our component stores its data in a custom table. Joomla expects an SQL file that runs during installation and another that runs on removal. We keep both files in the sql folder of the admin package.

We name the file install.mysql.utf8.sql. Joomla reads this file automatically because it is referenced in the manifest. The file contains a CREATE TABLE statement with an IF NOT EXISTS check to prevent errors during re‑installation.

CREATE TABLE IF NOT EXISTS `#__example_items` (
    `id`          INT(11) NOT NULL AUTO_INCREMENT,
    `title`       VARCHAR(255) NOT NULL,
    `alias`       VARCHAR(255) NOT NULL DEFAULT '',
    `description` TEXT,
    `state`       TINYINT(1) NOT NULL DEFAULT 0,
    `created`     DATETIME NOT NULL,
    `created_by`  INT(11) NOT NULL DEFAULT 0,
    `ordering`    INT(11) NOT NULL DEFAULT 0,
    PRIMARY KEY (`id`),
    KEY `idx_state` (`state`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

The table uses the Joomla prefix placeholder #__ so that the installer replaces it with the actual prefix of the site. We also store the user ID that created each record, which ties into Joomla’s ACL system for permission checks.

All database interactions go through the DatabaseDriver class. This abstraction provides a fluent query builder that works across MySQL and PostgreSQL. Below is a model method that fetches published items for the frontend view.

namespace Example\Component\Example\Site\Model;

use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;

class ItemsModel extends BaseDatabaseModel
{
    public function getItems()
    {
        $db    = $this->getDatabase();
        $query = $db->getQuery(true)
            ->select('*')
            ->from($db->quoteName('#__example_items'))
            ->where($db->quoteName('state') . ' = 1')
            ->order($db->quoteName('ordering') . ' ASC');

        $db->setQuery($query);
        return $db->loadObjectList();
    }
}

The manifest references the SQL scripts inside the <install> and <uninstall> blocks. The uninstall script typically contains a single DROP TABLE IF EXISTS statement so that removing the component also removes its data.

Building the Admin Interface

The back‑end part of the component lets administrators manage items. Joomla supplies a set of base classes that make list and edit screens straightforward to implement.

Our list view uses a model that extends ListModel. The model builds a query that supports sorting and filtering, while the view renders the results in a standard Joomla table layout with checkboxes and action buttons.

namespace Example\Component\Example\Administrator\Model;

use Joomla\CMS\MVC\Model\ListModel;

class ItemsModel extends ListModel
{
    protected function getListQuery()
    {
        $db    = $this->getDatabase();
        $query = $db->getQuery(true)
            ->select('a.*')
            ->from($db->quoteName('#__example_items', 'a'));

        $orderCol  = $this->state->get('list.ordering', 'a.ordering');
        $orderDirn = $this->state->get('list.direction', 'ASC');
        $query->order($db->escape($orderCol) . ' ' . $db->escape($orderDirn));

        return $query;
    }
}

Joomla reads an XML file to build the edit form. The file lives in admin/forms/item.xml and defines each field, its type, validation rules, and label.

<form>
    <fieldset name="basic">
        <field name="title" type="text"
               label="JGLOBAL_TITLE" required="true" />
        <field name="alias" type="text"
               label="JFIELD_ALIAS" />
        <field name="description" type="editor"
               label="JGLOBAL_DESCRIPTION"
               filter="safehtml" />
        <field name="state" type="list"
               label="JSTATUS" default="1">
            <option value="1">JPUBLISHED</option>
            <option value="0">JUNPUBLISHED</option>
        </field>
    </fieldset>
</form>

In the view’s display method we create a toolbar using ToolbarHelper. The helper adds standard buttons that map to controller tasks such as save, apply, and cancel.

namespace Example\Component\Example\Administrator\View\Item;

use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\ToolbarHelper;

class HtmlView extends BaseHtmlView
{
    public function display($tpl = null)
    {
        $this->item = $this->get('Item');
        $this->form = $this->get('Form');

        ToolbarHelper::title('Edit Item');
        ToolbarHelper::apply('item.apply');
        ToolbarHelper::save('item.save');
        ToolbarHelper::cancel('item.cancel');

        parent::display($tpl);
    }
}

We define the component’s access rules in access.xml. Joomla checks these rules before executing controller actions, ensuring that only authorized users can create, edit, or delete items. The ACL integrates with Joomla’s global permission system, so Super Administrators automatically inherit full access.

Building the Site (Frontend) View

The front‑end part displays items to visitors. We register a menu item type, set up a router for clean URLs, and create a view that pulls data from the model.

Menu item types are declared in the component’s XML metadata. When an administrator creates a new menu item, Joomla reads this metadata and presents the available views as options. For our component, we register a “List Items” view so that administrators can link any menu position to our item listing.

The router translates between human‑readable URLs and Joomla’s internal query parameters. In Joomla 4/5, we implement a router service that handles both building and parsing URL segments.

namespace Example\Component\Example\Site\Service;

use Joomla\CMS\Component\Router\RouterView;
use Joomla\CMS\Component\Router\Rules\MenuRules;
use Joomla\CMS\Component\Router\Rules\NomenuRules;
use Joomla\CMS\Component\Router\Rules\StandardRules;

class Router extends RouterView
{
    public function __construct($app = null, $menu = null)
    {
        parent::__construct($app, $menu);

        $items = new RouterViewConfiguration('items');
        $this->registerView($items);

        $item = new RouterViewConfiguration('item');
        $item->setKey('id')->setParent($items);
        $this->registerView($item);

        $this->attachRule(new MenuRules($this));
        $this->attachRule(new StandardRules($this));
        $this->attachRule(new NomenuRules($this));
    }
}

The frontend controller loads the appropriate view. The view pulls data from the model and passes it to a layout file stored in the tmpl directory. Making sure our URLs are clean is important for search engines — our Joomla SEO-friendly URLs guide covers the router configuration in more detail.

We place the default layout in tmpl/items/default.php. Joomla automatically looks for an override in the active template’s html/com_example/items folder. This gives site designers full control over the HTML without touching the component code. See our guide on Joomla template overrides to learn how to customize the output for any template.

<?php defined('_JEXEC') or die; ?>
<div class="com-example-items">
    <?php foreach ($this->items as $item) : ?>
        <article class="item">
            <h2>
                <a href="<?php echo JRoute::_('index.php?option=com_example&view=item&id=' . $item->id); ?>">
                    <?php echo $this->escape($item->title); ?>
                </a>
            </h2>
            <div class="description">
                <?php echo $item->description; ?>
            </div>
        </article>
    <?php endforeach; ?>
</div>

Packaging, Testing and Distribution

When the component is ready we bundle it into a ZIP file, create an update server, run a testing checklist, and optionally submit the package to the Joomla Extensions Directory.

All component files — PHP classes, XML manifests, language files, media assets — must follow the folder structure defined in the manifest. We compress the root folder into a ZIP archive that can be uploaded through the Joomla installer. The manifest’s method="upgrade" attribute ensures that reinstalling the ZIP updates existing files without deleting user data.

The update server XML tells Joomla where to look for newer versions. We host a small XML file on our server that lists each release with its version number, download URL, and minimum Joomla version. Once we add the update server URL to the manifest, Joomla will check for updates automatically and show a notification in the admin panel.

<updates>
    <update>
        <name>Example Component</name>
        <version>1.1.0</version>
        <tag>stable</tag>
        <downloadurl type="full">
            https://example.com/downloads/com_example-1.1.0.zip
        </downloadurl>
        <targetplatform name="joomla" version="4.[0123456789]" />
    </update>
</updates>

Before distributing the component, we run through a testing checklist:

  • Install on a fresh Joomla 4 and Joomla 5 site to verify compatibility.
  • Confirm that database tables are created during install and removed during uninstall.
  • Test all admin toolbar actions — save, apply, delete, and publish toggles.
  • Verify that ACL rules prevent unauthorized access to edit and delete actions.
  • Check that frontend URLs are clean and compatible with Joomla SEO-friendly URLs.
  • Validate that template overrides render without PHP notices or warnings.
  • Review Joomla file permissions to ensure the installer sets correct ownership.
  • Run through the Joomla security checklist for any extension‑level vulnerabilities.

To submit to the Joomla Extensions Directory, we create an account, upload the ZIP, and fill out the required metadata including a description, category, and screenshots. The JED team reviews the package for coding standards and security before listing it publicly.

For a broader overview of Joomla and its ecosystem, see our Joomla 4 tutorial. The official source code is available on the Joomla CMS GitHub repository, which is an excellent reference when studying how core components are structured.

FAQ

What PHP version do I need for Joomla component development?

Joomla 4 requires PHP 7.4 or newer, while Joomla 5 moves the minimum to PHP 8.1. We recommend developing against the latest stable PHP release that matches the Joomla version you intend to support, so your component benefits from current language features and security patches.

Can I convert a Joomla 3 component to Joomla 4 or 5?

Yes. The MVC pattern remains the same at a conceptual level, but you need to replace deprecated classes such as JModel and JView with their namespaced equivalents, update the XML manifest to reference a service provider, and adjust the routing to use the newer router service classes.

How long does it take to build a Joomla component?

Time varies with complexity. A simple CRUD component with a list view and an edit form can be assembled in a few hours. A feature‑rich extension with custom field types, API endpoints, and multi‑language support may take several days or even weeks of planning, coding, and testing.

What is the difference between a component and a module in Joomla?

A component is a full‑page application that handles a specific type of content and occupies the main content area of the page. A module is a lightweight block that displays data in a fixed template position, such as a sidebar login form or a footer navigation list. Components have their own admin screens and database tables, while modules typically rely on existing data sources.

Do I need to know MVC to build a Joomla component?

Understanding the MVC pattern is essential because Joomla’s entire extension architecture is built around it. The framework expects your code to separate data handling (model), output (view), and user actions (controller). Without this knowledge, we would end up fighting the framework instead of working with it, which makes maintenance and upgrades significantly harder.

Marco Vasquez
Written By

Marco Vasquez

Developer Relations

Marco is a full-stack developer and Joomla contributor with deep expertise in template development, module creation, and Joomla 5 architecture. He translates complex technical concepts into clear, actionable tutorials that developers at every level can follow.

Last Updated: April 19, 2026
🇬🇧 English | 🇸🇪 Svenska | 🇫🇮 Suomi | 🇫🇷 Français