Theme extensions control the visual appearance and layout of your OpenCart store. They provide templates, stylesheets, and assets that define how your store looks and feels to customers.
What Are Theme Extensions?
Theme extensions in OpenCart are template systems that:
Define the visual layout and styling of your store
Provide customizable templates for all pages
Include CSS, JavaScript, and image assets
Support responsive design for mobile devices
Can be customized without affecting functionality
Theme extensions are located in upload/extension/[vendor]/catalog/view/theme/ and configured through admin/controller/extension/[vendor]/theme/
Built-in Theme
OpenCart 4 includes a default theme called “Basic”:
Basic Theme The default OpenCart theme with a clean, modern design that’s fully responsive and customizable
Basic Theme Location
Location: admin/controller/extension/opencart/theme/basic.php:1
The basic theme provides:
Responsive grid layout
Mobile-friendly navigation
Product display templates
Checkout flow templates
Account management pages
Theme Architecture
Directory Structure
extension/[vendor]/
├── admin/
│ └── controller/
│ └── theme/
│ └── [theme_name].php # Theme configuration
└── catalog/
└── view/
├── template/
│ └── [theme_name]/
│ ├── common/
│ │ ├── header.twig
│ │ ├── footer.twig
│ │ └── menu.twig
│ ├── product/
│ │ ├── product.twig
│ │ ├── category.twig
│ │ └── search.twig
│ └── checkout/
│ └── checkout.twig
├── stylesheet/
│ └── [theme_name].css
└── javascript/
└── [theme_name].js
Admin Controller
namespace Opencart\Admin\Controller\Extension\Opencart\Theme ;
class Basic extends \Opencart\System\Engine\ Controller {
public function index () : void {
$this -> load -> language ( 'extension/opencart/theme/basic' );
$this -> document -> setTitle ( $this -> language -> get ( 'heading_title' ));
// Configuration form
$data [ 'save' ] = $this -> url -> link ( 'extension/opencart/theme/basic.save' );
$data [ 'back' ] = $this -> url -> link ( 'marketplace/extension' , '&type=theme' );
// Theme settings
$data [ 'theme_basic_product_limit' ] =
$this -> config -> get ( 'theme_basic_product_limit' ) ?: 15 ;
$data [ 'theme_basic_image_width' ] =
$this -> config -> get ( 'theme_basic_image_width' ) ?: 228 ;
$data [ 'theme_basic_image_height' ] =
$this -> config -> get ( 'theme_basic_image_height' ) ?: 228 ;
// Color scheme
$data [ 'theme_basic_primary_color' ] =
$this -> config -> get ( 'theme_basic_primary_color' ) ?: '#3498db' ;
$data [ 'theme_basic_secondary_color' ] =
$this -> config -> get ( 'theme_basic_secondary_color' ) ?: '#2c3e50' ;
// Status
$data [ 'theme_basic_status' ] =
$this -> config -> get ( 'theme_basic_status' );
$this -> response -> setOutput (
$this -> load -> view ( 'extension/opencart/theme/basic' , $data )
);
}
public function save () : void {
$this -> load -> language ( 'extension/opencart/theme/basic' );
$json = [];
if ( ! $this -> user -> hasPermission ( 'modify' , 'extension/opencart/theme/basic' )) {
$json [ 'error' ] = $this -> language -> get ( 'error_permission' );
}
if ( ! $json ) {
$this -> load -> model ( 'setting/setting' );
$this -> model_setting_setting -> editSetting (
'theme_basic' ,
$this -> request -> post
);
$json [ 'success' ] = $this -> language -> get ( 'text_success' );
}
$this -> response -> addHeader ( 'Content-Type: application/json' );
$this -> response -> setOutput ( json_encode ( $json ));
}
}
Template Engine (Twig)
OpenCart uses Twig as its template engine. Twig templates use .twig extension and provide:
Template inheritance
Variable output
Control structures (loops, conditionals)
Filters and functions
Automatic escaping for security
Basic Template Structure
{# common/header.twig #}
<! DOCTYPE html >
< html dir = "{{ direction }}" lang = "{{ lang }}" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > {{ title }} </ title >
< base href = "{{ base }}" />
{% if description %}
< meta name = "description" content = "{{ description }}" />
{% endif %}
{% if keywords %}
< meta name = "keywords" content = "{{ keywords }}" />
{% endif %}
{# Stylesheets #}
{% for style in styles %}
< link href = "{{ style . href }}" rel = "stylesheet" media = "{{ style . media }}" />
{% endfor %}
{# Scripts #}
{% for script in scripts %}
< script src = "{{ script . href }}" {% if script . postion == 'header' %}{% endif %} ></ script >
{% endfor %}
</ head >
< body >
< header >
< div class = "container" >
< div class = "row" >
< div class = "col-sm-4" >
< div id = "logo" >
{% if logo %}
< a href = "{{ home }}" >< img src = "{{ logo }}" title = "{{ name }}" alt = "{{ name }}" /></ a >
{% else %}
< h1 >< a href = "{{ home }}" > {{ name }} </ a ></ h1 >
{% endif %}
</ div >
</ div >
< div class = "col-sm-8" >
{{ search }}
{{ cart }}
</ div >
</ div >
</ div >
</ header >
{{ menu }}
Product Template Example
{# product/product.twig #}
{{ header }}
< div class = "container" >
< div class = "row" >
< div id = "content" class = "col-sm-12" >
< div class = "row" >
< div class = "col-sm-6" >
{# Product images #}
< div class = "product-images" >
{% if image %}
< img src = "{{ image }}" alt = "{{ heading_title }}" class = "img-responsive" />
{% endif %}
{% if images %}
< div class = "thumbnails" >
{% for img in images %}
< a href = "{{ img . popup }}" class = "thumbnail" >
< img src = "{{ img . thumb }}" alt = "{{ heading_title }}" />
</ a >
{% endfor %}
</ div >
{% endif %}
</ div >
</ div >
< div class = "col-sm-6" >
{# Product info #}
< h1 > {{ heading_title }} </ h1 >
{% if price %}
< ul class = "list-unstyled" >
< li >
< h2 > {{ price }} </ h2 >
</ li >
{% if special %}
< li > Regular: < span style = "text-decoration: line-through;" > {{ special }} </ span ></ li >
{% endif %}
</ ul >
{% endif %}
< div id = "product" >
{# Options #}
{% if options %}
{% for option in options %}
< div class = "form-group" >
< label > {{ option . name }} </ label >
{# Render option input based on type #}
</ div >
{% endfor %}
{% endif %}
{# Quantity #}
< div class = "form-group" >
< label > {{ text_qty }} </ label >
< input type = "text" name = "quantity" value = "{{ minimum }}" class = "form-control" />
</ div >
{# Add to cart button #}
< button type = "button" id = "button-cart" class = "btn btn-primary btn-lg" >
{{ button_cart }}
</ button >
</ div >
</ div >
</ div >
{# Product description tabs #}
< div class = "row" >
< div class = "col-sm-12" >
< ul class = "nav nav-tabs" >
< li class = "active" >< a href = "#tab-description" > {{ tab_description }} </ a ></ li >
{% if attribute_groups %}
< li >< a href = "#tab-specification" > {{ tab_attribute }} </ a ></ li >
{% endif %}
{% if review_status %}
< li >< a href = "#tab-review" > {{ tab_review }} </ a ></ li >
{% endif %}
</ ul >
< div class = "tab-content" >
< div class = "tab-pane active" id = "tab-description" >
{{ description }}
</ div >
</ div >
</ div >
</ div >
</ div >
</ div >
</ div >
{{ footer }}
Theme Configuration
Themes can be configured with various settings:
Common Theme Settings
// Product grid columns
theme_ { name } _product_limit = 15
// Image dimensions
theme_ { name } _image_width = 228
theme_ { name } _image_height = 228
theme_ { name } _thumb_width = 80
theme_ { name } _thumb_height = 80
// Popup image size
theme_ { name } _popup_width = 800
theme_ { name } _popup_height = 800
// Primary brand color
theme_ { name } _primary_color = '#3498db'
// Secondary color
theme_ { name } _secondary_color = '#2c3e50'
// Accent colors
theme_ { name } _success_color = '#27ae60'
theme_ { name } _warning_color = '#f39c12'
theme_ { name } _danger_color = '#e74c3c'
// Font family
theme_ { name } _font_family = 'Open Sans'
// Font sizes
theme_ { name } _font_size_base = 14
theme_ { name } _font_size_h1 = 36
theme_ { name } _font_size_h2 = 30
Customizing Themes
Override Templates
Never modify core extension files directly. Always create a custom theme or use the theme override system.
To customize templates:
Create Custom Theme : Copy theme directory with new name
Modify Templates : Edit .twig files as needed
Update Styles : Modify CSS in stylesheet/ directory
Configure Theme : Set as active in admin panel
Theme Override System
OpenCart loads templates in this order:
catalog/view/theme/[active_theme]/template/...
catalog/view/theme/default/template/...
Extension-specific templates
Custom CSS
Add custom CSS without modifying theme files:
Admin Panel Method:
Go to Design → Theme Editor
Select your theme
Add custom CSS in the stylesheet editor
File Method:
/* catalog/view/theme/mytheme/stylesheet/custom.css */
/* Override header styles */
header {
background-color : #2c3e50 ;
padding : 20 px 0 ;
}
/* Customize product cards */
.product-card {
border : 1 px solid #ecf0f1 ;
border-radius : 8 px ;
padding : 15 px ;
transition : transform 0.3 s ;
}
.product-card:hover {
transform : translateY ( -5 px );
box-shadow : 0 5 px 15 px rgba ( 0 , 0 , 0 , 0.1 );
}
/* Button styles */
.btn-primary {
background-color : #3498db ;
border : none ;
padding : 12 px 30 px ;
border-radius : 4 px ;
}
.btn-primary:hover {
background-color : #2980b9 ;
}
Responsive Design
Ensure your theme works on all devices:
/* Mobile-first approach */
.product-grid {
display : grid ;
grid-template-columns : 1 fr ;
gap : 20 px ;
}
/* Tablet */
@media ( min-width : 768 px ) {
.product-grid {
grid-template-columns : repeat ( 2 , 1 fr );
}
}
/* Desktop */
@media ( min-width : 992 px ) {
.product-grid {
grid-template-columns : repeat ( 3 , 1 fr );
}
}
/* Large desktop */
@media ( min-width : 1200 px ) {
.product-grid {
grid-template-columns : repeat ( 4 , 1 fr );
}
}
Developing Custom Themes
Theme Package Structure
extension/myvendor/
├── install.json
├── admin/
│ ├── controller/
│ │ └── theme/
│ │ └── mytheme.php
│ ├── language/
│ │ └── en-gb/
│ │ └── theme/
│ │ └── mytheme.php
│ └── view/
│ └── template/
│ └── theme/
│ └── mytheme.twig
└── catalog/
└── view/
├── template/
│ └── mytheme/
│ ├── common/
│ ├── product/
│ ├── checkout/
│ └── account/
├── stylesheet/
│ └── mytheme.css
└── javascript/
└── mytheme.js
Install Manifest
{
"name" : "My Custom Theme" ,
"description" : "A beautiful, responsive theme for OpenCart" ,
"version" : "1.0.0" ,
"author" : "Your Name" ,
"link" : "https://yoursite.com"
}
Theme Controller Template
namespace Opencart\Admin\Controller\Extension\Myvendor\Theme ;
class Mytheme extends \Opencart\System\Engine\ Controller {
public function index () : void {
$this -> load -> language ( 'extension/myvendor/theme/mytheme' );
$this -> document -> setTitle ( $this -> language -> get ( 'heading_title' ));
$data [ 'save' ] = $this -> url -> link ( 'extension/myvendor/theme/mytheme.save' );
$data [ 'back' ] = $this -> url -> link ( 'marketplace/extension' , '&type=theme' );
// Layout settings
$data [ 'theme_mytheme_product_limit' ] =
$this -> config -> get ( 'theme_mytheme_product_limit' ) ?: 12 ;
$data [ 'theme_mytheme_product_description_length' ] =
$this -> config -> get ( 'theme_mytheme_product_description_length' ) ?: 100 ;
// Image dimensions
$data [ 'theme_mytheme_image_product_width' ] =
$this -> config -> get ( 'theme_mytheme_image_product_width' ) ?: 228 ;
$data [ 'theme_mytheme_image_product_height' ] =
$this -> config -> get ( 'theme_mytheme_image_product_height' ) ?: 228 ;
// Color scheme
$data [ 'theme_mytheme_color_primary' ] =
$this -> config -> get ( 'theme_mytheme_color_primary' ) ?: '#007bff' ;
$data [ 'theme_mytheme_color_secondary' ] =
$this -> config -> get ( 'theme_mytheme_color_secondary' ) ?: '#6c757d' ;
// Custom options
$data [ 'theme_mytheme_enable_quickview' ] =
$this -> config -> get ( 'theme_mytheme_enable_quickview' );
$data [ 'theme_mytheme_enable_wishlist' ] =
$this -> config -> get ( 'theme_mytheme_enable_wishlist' );
$data [ 'theme_mytheme_enable_compare' ] =
$this -> config -> get ( 'theme_mytheme_enable_compare' );
// Status
$data [ 'theme_mytheme_status' ] =
$this -> config -> get ( 'theme_mytheme_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/myvendor/theme/mytheme' , $data )
);
}
public function save () : void {
$this -> load -> language ( 'extension/myvendor/theme/mytheme' );
$json = [];
if ( ! $this -> user -> hasPermission ( 'modify' , 'extension/myvendor/theme/mytheme' )) {
$json [ 'error' ] = $this -> language -> get ( 'error_permission' );
}
if ( ! $json ) {
$this -> load -> model ( 'setting/setting' );
$this -> model_setting_setting -> editSetting (
'theme_mytheme' ,
$this -> request -> post
);
$json [ 'success' ] = $this -> language -> get ( 'text_success' );
}
$this -> response -> addHeader ( 'Content-Type: application/json' );
$this -> response -> setOutput ( json_encode ( $json ));
}
}
Best Practices
Mobile-First Design for mobile devices first, then enhance for larger screens
Performance Optimize images, minify CSS/JS, and use lazy loading
Accessibility Follow WCAG guidelines for accessible design
Browser Testing Test across different browsers and devices
Use browser developer tools to:
Inspect and modify CSS in real-time
Debug JavaScript
Test responsive layouts
Monitor performance
Design → Theme Editor
Built-in theme editor in admin panel:
Go to Design → Theme Editor
Select theme and file
Edit directly in browser
Changes are saved immediately
Use Theme Editor cautiously. Always backup files before making changes.
Troubleshooting
Theme Not Appearing
Check installation : Is theme extension installed?
Check status : Is theme enabled in settings?
Clear cache : Dashboard → Developer Settings → Refresh
Check file permissions : Ensure files are readable
Styles Not Loading
Check file path : Verify CSS file location
Check browser console : Look for 404 errors
Clear browser cache : Force refresh (Ctrl+F5)
Check minification : Disable CSS minification if enabled
Template Errors
Check Twig syntax : Ensure proper tag closing
Check variables : Verify variable names match controller
Check error log : Review system/storage/logs/error.log
Enable debug : Set display_errors in config for development
Next Steps