On September 29, 2026, a
discussion #15713
with the development plan for the 4.2.x.x branch was opened in the
opencart/opencart repository. It was started by one of the project's
collaborators, and within four days developers joined in with ideas on
reducing the distribution size, counts of jQuery calls, and proposals for
the admin panel. The plan has not been approved yet, and right now the
community is deciding which changes store owners and extension authors will
have to live with in the next release.
We have been developing OpenCart modules for a long time, and we look at
this plan through the eyes of someone who will have to adapt their own
extension catalog to the new version. So let's go through each proposal:
what it changes in the database, frontend and admin panel, where it creates
compatibility risks, and which solution, in my view, is the most balanced.
Database Structure Changes Again
The first item in the plan is to bring the 4.2 database schema closer to the
main branch. Three tables are involved: oc_product_tag for product tags,
oc_manufacturer_description for multilingual manufacturer descriptions, and
oc_product_code for product identifiers such as GTIN, EAN or MPN. For module
developers this is good news: the fewer differences between branches, the
easier it is to maintain a single codebase for several core versions.
The most interesting question concerns oc_product_code. In main, this table
has changed for the second time since 4.1 and uses an auto-increment primary
key, product_code_id. The discussion proposes a composite key,
PRIMARY KEY (product_id, identifier_id), following the same
pattern that oc_product_description and oc_product_attribute have used for
years. I fully support this option.
The reason lies in how OpenCart saves a product. The editProduct method in
the admin model traditionally deletes all related records and inserts them
again. With an auto-increment key, a product with three codes will burn
through three thousand counter values after a thousand saves, and every code
will receive a new product_code_id each time. Overflowing INT UNSIGNED with
its 4.29 billion values is no threat to a store. The real problem will show
up in modules: a Google Merchant Center feed, a 1C sync or a supplier import
that stores a reference to product_code_id loses the link after every
product edit in the admin panel.
A composite key gives each record a stable address and allows atomic
INSERT ... ON DUPLICATE KEY UPDATE queries without a preceding
SELECT. The "one code of each type per product" constraint matches the
requirements of GS1 and marketplaces, where one product item has one GTIN.
Rare cases with several barcodes for different packaging are more honestly
handled through product variants, which already exist in version 4.
Upgrading to jQuery 4.0.0
The second proposal is to upgrade jQuery from 3.7.1 to 4.0.0. The
final release
came out on January 17, 2026, almost ten years after the previous major
version. The library team dropped support for IE 10 and older browsers,
removed deprecated methods and cut the file size by more than 3 KB gzipped.
Trusted Types support was added, which is useful for stores with a strict
Content Security Policy.
For extensions this means very concrete work. jQuery.trim,
jQuery.isArray, jQuery.parseJSON,
jQuery.now and jQuery.isFunction are gone, and
push, sort and splice were removed from the prototype. The focus and blur
events now fire in the order defined by the W3C specification, which may
change form validation behavior in checkout modules. Automatic promotion of
AJAX requests to JSONP has also been removed.
In code carried over from OpenCart 2.x and 3.x, $.trim and
$.isArray are the most common. A plain project-wide search
finds them, and replacing them with String.prototype.trim() and
Array.isArray() takes minutes. OCMOD patches that inject
JavaScript into other templates are harder: such code becomes visible only
after the modifications are applied.
I consider the decision to move to jQuery 4 in 4.2 the right one. Postponing
the upgrade to version 4.3 would only increase the amount of outdated code
in the ecosystem. I would add one thing to the plan: an option to load
jQuery Migrate in developer mode. This plugin logs a console warning for
every removed call and gives module authors a ready-made list of fixes right
on a live store.
Or No jQuery at All
A count of calls in the 4.x code shows that the storefront depends on jQuery
only slightly. All the heavy dependency is concentrated in the admin panel.
| Part | Files using jQuery | $() calls | jQuery plugins |
| catalog | 25 | 552 | magnific-popup |
| admin | 187 | 2958 | jquery-ui, flot, jqvmap, magnific-popup |
The catalog uses only basic methods: .on(), .find(), .closest(), addClass,
$.ajax. There is not a single fadeIn, slideUp or animate call in the
storefront code, and these animations are usually the hardest part to
rewrite in plain JavaScript. Bootstrap 5 has worked without jQuery since its
release, so the only dependent storefront component left is magnific-popup
for the product gallery. Once these 25 files are rewritten, the default
theme will stop loading about 88 KB of minified library code for every
visitor.
Thousands of extensions in the OpenCart Marketplace assume jQuery is already
on the page. If the core drops the library in one move, module authors will
start bundling their own copies of jQuery. On a store with a dozen
extensions, this means three or four library versions on a single page,
event handler conflicts and an outcome worse than today's.
I consider the most reasonable option to be the compromise the discussion
eventually arrived at. In 4.2, the core rewrites its own storefront code in
plain JavaScript in small batches (account, checkout, product page, CMS),
jQuery 4.0.0 remains available for extensions, and full removal moves to
4.3. The admin panel stays on jQuery for a long time: jquery-ui, flot and
jqvmap have no ready replacements, and script weight for a few store
managers has no effect on SEO or conversion.
Once the storefront core no longer depends on jQuery, it becomes possible to
load the library with the defer attribute or only when an extension requests
it. A dependency flag in the call that adds a script to the document is
enough for this. A store without legacy modules will immediately get a clean
page, which will noticeably improve Core Web Vitals on mobile devices.
JavaScript in Twig Templates and OCMOD Compatibility
The idea of moving all inline JavaScript from .twig files into external .js
files was discussed separately. Architecturally this is the right long-term
direction, and the right time to introduce it is after 4.2. There are two
reasons for this position.
The first reason is technical. The <script> blocks of
almost every storefront template contain Twig variables: translated strings,
route URLs, the language code. A line like
{{ text_shipping_method|escape('js') }} simply stops working in
a static file. First, the project has to decide how data will reach the
script: through data attributes, a global configuration object, rendering
.js files through Twig, or a client-side template engine. The last option is
the most dangerous, since it moves rendering into the browser and loses
server-side rendering for those blocks, which hurts indexing and first paint
speed.
The second reason concerns the ecosystem. Almost no store runs on a "clean"
OpenCart, and many modules use OCMOD or VQMOD to append their code directly
to the inline scripts of the cart or checkout templates. If that code moves
to common.js, every extension will patch the same file, and the very first
conflict between two modifications will break the entire storefront.
In my view, a combination of three solutions fits 4.3 best. Element-specific
data is passed through data attributes, translations and URLs are rendered
by the server into a single global configuration object, and the logic lives
in files and fires its own events through the native CustomEvent. An
extension then subscribes to an event such as adding a product to the cart
or changing the shipping method and patches no other file. An extra benefit
is the ability to enable a strict Content Security Policy without
'unsafe-inline', which is practically unattainable for OpenCart today. Such
a system needs careful design and event documentation, so it should be built
into the next version together with the removal of jQuery.
Excess Megabytes in the Package and a Shared assets Folder
The upload folder in the current 4.x branch weighs about 62 MB, and 15 to 20
MB of it is never loaded by the browser. For a store on shared hosting,
where files are still often uploaded over FTP, every thousand extra files
means a longer installation and a higher chance of an interrupted upload. An
audit of the distribution revealed these sources of excess weight:
-
Source map files: 37 files totaling 9.9 MB, a large share of them in the
install folder, which is deleted after installation.
-
Six Bootstrap JS builds in each folder, of which templates load only
bootstrap.bundle.min.js at 79 KB. Together with the map files, this is
about 2.4 MB per folder and 4.8 MB for admin and catalog combined.
-
The flot/examples demo folder: 65 files totaling 1.2 MB, carried over
from the library's original distribution.
-
Font Awesome at 784 KB, duplicated three times in admin, catalog and
install.
The flot/examples folder and the bootstrap.esm builds are a clear case: ESM
versions are meant for bundlers such as Webpack or Vite, which OpenCart does
not use, and they can be removed with no consequences. The Bootstrap 5.3.8
SCSS sources must stay in the core, because third-party themes and modules
with their own styling rely on them. A small and very useful fix belongs to
the same area: the storefront serves an unminified bootstrap.css generated
by scssphp. A single line,
$scss->setOutputStyle(\ScssPhp\ScssPhp\OutputStyle::COMPRESSED);, in catalog/controller/startup/sass.php reduces the CSS on every store
page.
For source map files, I see a solution that satisfies both camps. The files
stay in the repository for developers, and a
*.map export-ignore line in .gitattributes excludes them from
the ZIP archives GitHub builds for releases. The same mechanism applies to
demo files and tests.
The duplication problem is solved by a shared library folder accessible from
both admin and catalog. In the main branch it already exists as
upload/assets, and for 4.2 I would take exactly this option: modules won't
have to relearn paths a second time after the move to the next major
version. The Laravel-style approach with a public folder requires changing
the site root on the server. On many budget hosting plans the document root
is fixed, so this kind of reorganization logically belongs to OpenCart 5.
Moving libraries into a shared folder will affect modules that load jQuery,
Bootstrap or Font Awesome via a hardcoded path such as
catalog/view/javascript/jquery/. There are plenty of such extensions on the
market, and their authors should check their templates in advance.
Category Tree for Large Catalogs
The only proposal for the admin interface concerned categories: a manager
with mouse drag-and-drop for branches and a checkbox tree in the product
form with filtering by name. The arguments for this idea are serious:
OpenCart allows unlimited category nesting, and in a catalog of 600
categories with five nesting levels, the standard list with a sort_order
field turns into a puzzle.
The concern about scale is also valid. Auto parts and building materials
stores keep thousands of subcategories, and a product form with a full
checkbox tree becomes a huge HTML document that noticeably slows down the
browser on a manager's average laptop.
A lazy-loading tree reconciles both positions. Child nodes are loaded via an
AJAX request when a branch is expanded, a name search returns only matches
together with their path, and the DOM holds a few dozen elements at any
given moment. The current autocomplete field should stay alongside it for
those who know the exact category name.
Drag-and-drop has a nuance that is rarely mentioned. OpenCart also stores
the hierarchy in the oc_category_path table, which records the full chain of
ancestors for every category. Moving a branch with a hundred descendants
requires recalculating paths for the whole branch and clearing the SEO URL
cache. In a drag-and-drop interface this has to happen in a single
transaction, otherwise an accidental mouse movement will leave the catalog
in an inconsistent state. That's why I would first release the category
manager as a separate extension and move it into the core after it has been
proven on real large catalogs.
Preparing Modules for OpenCart 4.2
The development plan will keep changing, and some directions are already
clear: jQuery 4.0.0, the database schema from main and the gradual freeing
of the storefront from jQuery. It pays for an extension developer to start
adapting before the release, while support is not yet flooded with client
requests after the update. I have set myself the following order of steps:
-
Search through the code and OCMOD XML files and replace
$.trim, $.isArray, $.parseJSON,
$.isFunction and $.now with native
equivalents. These replacements are also compatible with jQuery 3.7.1,
so they can ship in current module versions.
-
Set up a test copy of a store on 4.1, swap jQuery for 4.0.0 together
with jQuery Migrate, and go through the cart, checkout and product page
with the browser console open.
-
Remove hardcoded paths to jQuery, Bootstrap and Font Awesome from
templates. After the libraries move to a shared folder, such links will
return 404.
-
Write new JavaScript in plain JS with fetch, querySelector and
classList. Code without a jQuery dependency will survive both 4.2 and
4.3 without rework.
-
In feed and sync modules, address product codes by the pair of
product_id and identifier type. Storing product_code_id in your own
tables is unsafe under any version of the final schema.
-
Minimize OCMOD patches inside other
<script> blocks.
Handlers attached from your own file through event delegation on
document survive core template rewrites much better.
The discussion is open to everyone, and the opinion of extension developers
genuinely influences decisions there: it was the argument about thousands of
Marketplace modules that pushed full jQuery removal back to 4.3. If your
module depends on the oc_product_code structure, library paths or checkout
inline scripts, now is the best moment to describe your use case in the
GitHub thread.