OpenCart 4.2: Development Plan

OpenCartBot - 03 October 2026
OpenCart 4.2: Development Plan

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:

  1. 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.
  2. 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.
  3. Remove hardcoded paths to jQuery, Bootstrap and Font Awesome from templates. After the libraries move to a shared folder, such links will return 404.
  4. 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.
  5. 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.
  6. 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.


Products related to this post


Related Posts