Help us fix this page
If you found a broken link, missing page, or incorrect redirect, please let us know. Your report helps us improve the website for everyone.

OpenCart is a popular open‑source eCommerce platform known for its flexibility and user‑friendliness. With the release of OpenCart 4, developers have new opportunities to enhance and extend the platform’s functionality. This guide provides a thorough, up‑to‑date walkthrough of developing an extension for OpenCart 4, covering planning, development, testing, and deployment-with current best practices for 2026.
/extension/{vendor}/, replacing the scattered file placement used in previous versions.Tasks – background processes for operations that do not require immediate feedback, similar to scheduled jobs or cron tasks.Before writing any code, set up a local development environment with the correct OpenCart version, a web server, and essential tools.
OpenCart 4 requires:
mod_rewrite or NginxOpenCart 4 introduces significant changes compared to version 3, both in structure and in extension mechanisms.
OpenCart 4 follows a custom MVC‑A pattern-Model‑View‑Controller‑Action-built on modern PHP 8.0+ with namespaced classes. The framework emphasises modularity, extensibility through events, and a clean separation of concerns.
| Component | Responsibility |
|---|---|
| Model | Data access and business logic. Interacts with the database. |
| View | User interface and presentation. Uses the Twig templating engine. |
| Controller | Processes user input, orchestrates models and views, and returns responses. |
| Action | The specific method within a controller that handles a request. |
One of the most important changes in OpenCart 4 is the move to a self‑contained extension directory. In OpenCart 3, extension files were scattered across core directories like admin/controller/extension/module/. In OpenCart 4, your entire extension lives in a single folder under /extension/{vendor}/.
This structure makes extensions easier to develop, version, and maintain. The recommended structure is:
extension/
└── {vendor}/
└── {extension_name}/
├── admin/
│ ├── controller/
│ ├── model/
│ ├── view/
│ │ └── template/
│ └── language/
├── catalog/
│ ├── controller/
│ ├── model/
│ ├── view/
│ │ └── template/
│ └── language/
├── system/
│ └── library/ # For custom classes
├── install.sql # Database installation script
├── install.php # Installation logic
└── uninstall.sql # Database uninstallation scriptThe root namespace for extension controllers follows the pattern:Opencart\Admin\Controller\Extension\{Vendor}\{ExtensionName}\{ControllerName}.
OpenCart 4 provides three primary mechanisms for extending functionality.
The event system is the recommended way to extend OpenCart 4 without modifying core files. Events are triggered at key points in controller, model, and view execution. Extensions can register listeners that execute custom code when these events fire.
Events are registered in the extension’s installation method or via the Extension > Events admin interface. A typical event registration looks like:
// In your extension's install() method
$this->load->model('setting/event');
$this->model_setting_event->addEvent(
'your_extension_code',
'catalog/controller/common/header/before',
'extension/your_vendor/your_extension/event/header'
);Events are the primary extension mechanism for OpenCart 4, offering a cleaner and more secure alternative to file modifications.
Status update for 2026: OCMOD was removed in early OpenCart 4.0.x releases, but was reintroduced in OpenCart 4.1.0.0. The official GitHub release for 4.1.0.0 explicitly states “ADDED OCMOD back!!”.
OCMOD allows developers to modify core OpenCart files through XML‑based modifications without directly editing the core code. It intercepts file loading requests and substitutes original files with modified versions if they exist.
For OpenCart 4.1.x and newer, OCMOD is available and can be used alongside the event system. For versions between 4.0.0.0 and 4.0.2.3, OCMOD is not supported-extensions must use the event system exclusively.
Practical guidance for 2026:
Tasks were introduced in OpenCart 4.2. They are background processes for operations that do not require immediate feedback, aiming to speed up the overall experience.
Tasks are ideal for:
Tasks appear in the admin interface under Extension > Tasks after installation. They can be triggered manually or via cron jobs.
Before writing code, define:
Create the necessary directories under /extension/{vendor}/{extension_name}/. For a simple module, you typically need:
extension/
└── yourvendor/
└── examplemodule/
├── admin/
│ ├── controller/
│ │ └── module.php
│ ├── model/
│ │ └── module.php
│ ├── view/
│ │ └── template/
│ │ └── module.twig
│ └── language/
│ └── en-gb/
│ └── module.php
├── catalog/
│ ├── controller/
│ │ └── module.php
│ ├── model/
│ │ └── module.php
│ ├── view/
│ │ └── template/
│ │ └── module.twig
│ └── language/
│ └── en-gb/
│ └── module.php
├── install.sql
└── install.phpThe controller handles user requests and orchestrates the response. For an admin module controller, the file path would be:
extension/yourvendor/examplemodule/admin/controller/module.php
<?php
namespace Opencart\Admin\Controller\Extension\YourVendor\ExampleModule;
class Module extends \Opencart\System\Engine\Controller
{
public function index(): void
{
$this->load->language('extension/yourvendor/examplemodule/module');
$this->document->setTitle($this->language->get('heading_title'));
$data['breadcrumbs'] = $this->getBreadcrumbs();
// Load your settings from the database
$data['module_status'] = $this->config->get('module_example_status');
$data['header'] = $this->load->controller('common/header');
$data['column_left'] = $this->load->controller('common/column_left');
$data['footer'] = $this->load->controller('common/footer');
$this->response->setOutput($this->load->view('extension/yourvendor/examplemodule/module', $data));
}
protected function getBreadcrumbs(): array
{
$breadcrumbs = [];
$breadcrumbs[] = [
'text' => $this->language->get('text_home'),
'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'])
];
$breadcrumbs[] = [
'text' => $this->language->get('text_extension'),
'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module')
];
$breadcrumbs[] = [
'text' => $this->language->get('heading_title'),
'href' => $this->url->link('extension/yourvendor/examplemodule/module', 'user_token=' . $this->session->data['user_token'])
];
return $breadcrumbs;
}
}The model handles data access. For settings management:
extension/yourvendor/examplemodule/admin/model/module.php
<?php
namespace Opencart\Admin\Model\Extension\YourVendor\ExampleModule;
class Module extends \Opencart\System\Engine\Model
{
public function install(): void
{
// Create database tables
$this->db->query("
CREATE TABLE IF NOT EXISTS `" . DB_PREFIX . "example_module` (
`example_module_id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`status` TINYINT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`example_module_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
");
}
public function uninstall(): void
{
$this->db->query("DROP TABLE IF EXISTS `" . DB_PREFIX . "example_module`;");
}
public function getSettings(): array
{
$query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "example_module`");
return $query->rows;
}
}OpenCart 4 uses the Twig templating engine. Admin views go in the admin/view/template/ directory:
extension/yourvendor/examplemodule/admin/view/template/module.twig
{{ header }}{{ column_left }}
<div id="content">
<div class="page-header">
<div class="container-fluid">
<div class="float-end">
<button type="submit" form="form-module" data-toggle="tooltip" title="{{ button_save }}" class="btn btn-primary">
<i class="fa-solid fa-save"></i>
</button>
<a href="{{ back }}" data-toggle="tooltip" title="{{ button_back }}" class="btn btn-light">
<i class="fa-solid fa-reply"></i>
</a>
</div>
<h1>{{ heading_title }}</h1>
<ol class="breadcrumb">
{% for breadcrumb in breadcrumbs %}
<li class="breadcrumb-item">
<a href="{{ breadcrumb.href }}">{{ breadcrumb.text }}</a>
</li>
{% endfor %}
</ol>
</div>
</div>
<div class="container-fluid">
<div class="card">
<div class="card-header"><i class="fa-solid fa-pencil"></i> {{ text_edit }}</div>
<div class="card-body">
<form id="form-module" method="post" action="{{ save }}" data-oc-toggle="ajax">
<div class="row mb-3">
<label class="col-sm-2 col-form-label" for="input-status">{{ entry_status }}</label>
<div class="col-sm-10">
<div class="form-check form-switch form-switch-lg">
<input type="hidden" name="module_example_status" value="0"/>
<input type="checkbox" name="module_example_status" value="1" id="input-status" class="form-check-input"{% if module_example_status %} checked{% endif %}/>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{{ footer }}Language files provide translations for your extension. For English:
extension/yourvendor/examplemodule/admin/language/en-gb/module.php
<?php
// Heading
$_['heading_title'] = 'Example Module';
// Text
$_['text_extension'] = 'Extensions';
$_['text_success'] = 'Success: You have modified Example Module!';
$_['text_edit'] = 'Edit Example Module';
$_['text_footer_message'] = 'This is an example module for OpenCart 4.';
// Entry
$_['entry_status'] = 'Status';
// Error
$_['error_permission'] = 'Warning: You do not have permission to modify Example Module!';OpenCart 4 looks for install.sql and install.php in the extension root. The install.php file is executed during installation and can set up events, create database tables, and initialise settings.
extension/yourvendor/examplemodule/install.php
<?php
// Install script for Example Module
$installer = $this;
$installer->load->model('setting/event');
// Register an event listener
$installer->model_setting_event->addEvent(
'example_module',
'catalog/controller/common/header/before',
'extension/yourvendor/examplemodule/event/header'
);For the event system to work, your extension must register event listeners. This can be done in the install.php file or in the model_setting_event table.
Common event triggers:
| Event Trigger | Description |
|---|---|
catalog/controller/*/before | Before any catalog controller executes |
catalog/controller/*/after | After any catalog controller executes |
catalog/model/*/before | Before any catalog model executes |
catalog/model/*/after | After any catalog model executes |
catalog/view/*/before | Before a view is rendered |
admin/controller/*/before | Before any admin controller executes |
OpenCart 4 extensions are packed in a ZIP archive with a specific structure. The archive must contain the extension/ folder and cannot be zipped from a parent directory.
Packaging steps:
extension/{vendor}/{extension_name}/extension/ as the top‑level directoryImportant: Although OCMOD extensions still require the .ocmod.zip extension in some contexts, this naming convention is a remnant of OpenCart 3 and does not indicate active OCMOD functionality in all versions. For 4.1.x and newer, the installer handles both .zip and .ocmod.zip files.
Test individual components (controllers, models) in isolation. While OpenCart does not include a built‑in testing framework, you can use PHPUnit for custom tests.
Test how your extension interacts with OpenCart’s core and other extensions. Verify:
Gather feedback from real users to identify usability issues and improve the user experience.
Ensure your extension does not degrade store performance. Monitor:
Package your extension as a ZIP archive with the correct structure. Include:
Provide comprehensive documentation covering:
To distribute your extension via the OpenCart Marketplace, follow the submission guidelines on the OpenCart website. Ensure your extension meets the marketplace’s quality and security standards.
Address user-reported issues promptly. Regular updates build trust with your users.
Stay informed about OpenCart releases. Test your extension against new versions and release updates as needed. OpenCart 4.x continues to evolve-new features like Tasks and restored OCMOD functionality mean extension developers need to keep current.
Provide a clear support channel (email, forum, or ticketing system) and respond to inquiries promptly.
| Practice | Why It Matters |
|---|---|
| Follow OpenCart’s coding standards | Ensures compatibility and maintainability |
| Use the event system where possible | More maintainable and less prone to conflicts than file modifications |
| Validate and sanitise all user input | Prevents security vulnerabilities |
| Use the self‑contained extension directory | Cleaner structure, easier to version and maintain |
| Test on multiple PHP versions | OpenCart 4 runs on PHP 8.0+; ensure compatibility |
| Write clear documentation | Reduces support requests and improves user satisfaction |
| Use namespaces correctly | Follow the Opencart\Admin\Controller\Extension\{Vendor}\... pattern |
/extension/{vendor}/, replacing the scattered file placement of OpenCart 3.OpenCart 4 represents a significant evolution of the platform. The move to a self‑contained extension directory, the restoration of OCMOD, the introduction of Tasks, and the continued reliance on the event system give developers a flexible and powerful toolkit for extending the platform.
For new extensions, the event system should be your first choice. It is cleaner, more maintainable, and less likely to cause conflicts than file‑based modifications. If you are targeting OpenCart 4.1.x or newer, you have the additional option of using OCMOD for modifications that events cannot cover.
Careful planning, thorough testing, and ongoing maintenance are the keys to a successful extension. With OpenCart 4’s modern architecture and 2026’s current features, the possibilities for extension development are greater than ever.
Need help building an OpenCart 4 extension? Playful Sparkle has been engineering digital products since 2004, offering Web Development, App Development, and eCommerce solutions including OpenCart. Our team can help you design, develop, and maintain high‑quality OpenCart extensions. Contact us to discuss your project.