Two ecosystems,
One big event.
November 5, 2026. Lyon
6 min. read
Categories: Ecosystem News Technical
Sylius 2.3 is here, with dark mode, PHP grids, and improved payments 

Sylius 2.3 is the first release built for Symfony 8, and it is a lot more than a dependency bump. The admin panel gets a dark theme. Every core grid is now configurable in PHP. Promotions finally behave the way multi-channel merchants actually need them to. And the Payment Request API leaves experimental status for good.

A modern foundation

Sylius 2.3 requires PHP 8.3 and adds Symfony 8 support alongside 6.4 and 7.4 (#19072). You can upgrade Sylius first and move to Symfony 8 on your own schedule.

The Doctrine stack widened at the same time (#19064, #19133): DBAL 3.10 or 4.4, DoctrineBundle 2.18 or 3.2, ORM 2.20 or 3.6, Persistence 3.4 or 4.2. Sylius ships its own ObjectType DBAL type so payment tokens and payment request payloads keep working on DBAL 4, where the built in object type is gone.

Symfony UX 3.0 is supported too (#19189). The five UX packages Sylius uses, Stimulus Bundle, Autocomplete, Icons, Live Component and Twig Component, now accept ^2.36 || ^3.0. It is a union, not a bump: your project stays on UX 2 until you decide to move, and UX 3 requires PHP 8.4 and Symfony 7.4 on its own terms. The one thing to know is that twig_component.defaults becomes mandatory under UX 3, and Sylius Standard ships it for you.

Three dependencies are out: 

  • ProxyManager is replaced by native PHP lazy objects through symfony/var-exporter (#19062). Less magic, faster boot
  • Gaufrette is gone (#18880). Flysystem has been the default adapter since 2.0, so this is cleanup you will not notice
  • behat/transliterator is deprecated in favour of symfony/string for slug generation (#18947)

API Platform 5 support

API Platform 5 is supported as well (#19248). Sylius accepts both ^4.3 and ^5.0, so you can choose when to upgrade. API Platform 5 requires Symfony 7.4 or 8; projects on Symfony 6.4 remain on API Platform 4.

The Shop and Admin APIs work the same on both versions, with one change to check if you consume them: a payload containing a value of the wrong type now returns 422 with a constraint violation instead of 400.

Dark mode in the admin panel 🌓

The most requested piece of polish is in (#18981). A toggle in the navbar switches the whole admin between light and dark. It respects the operating system setting through prefers-color-scheme and remembers the choice, so the people who spend eight hours a day in your admin panel can finally stop squinting.

This one came from our Professional Solution Partner ACSEO, with follow-up polish in #19163 and #19186.

Grids configured in PHP

Around 35 core grids now exist as PHP classes instead of YAML (#17675, #15419, and roughly thirty migration PRs on top). You get autocompletion, static analysis, and refactoring support on something that used to be a wall of YAML keys.

PHP configuration is the default in 2.3 and the direction for Sylius 3.0. Nothing breaks on day one: the sylius_core.grid.use_legacy_config switch keeps your YAML grids running, globally or per grid, so you can migrate one grid at a time. A grid loads from one source only, PHP and YAML definitions are never merged.

Most of this work is by mamazu, with Simon Krull and Loïc Frémont on the rest.

Promotions built for real campaigns 📈

Rules and actions per channel (#19095). One promotion, independent configuration per channel. “Buy 2 in the US store, buy 5 in the UK store” is now a single promotion instead of two that drift apart. Six new rules and two new actions ship with it, all opt in, nothing in the core is replaced.

Usage tracking you can turn off (#18966). Promotions and coupons get a trackUsage flag. Switch it off and the promotion runs without counting toward usage limits, which is exactly what you want for evergreen campaigns and internal testing.

Comparison operators on Cart Quantity and Item Total (#12299). These two rules were “greater than or equal” and nothing else. Now you pick the operator. Worth noting: this PR was opened in 2020 by Francis Hilaire, and it finally landed.

The promotion form remembers what you removed (#19101). Remove a rule, change your mind, add it back: the configuration is still there instead of blank. Closes a five-year-old issue.

Usage counters no longer race (#18921). Optimistic locking on promotions and coupons means concurrent checkouts cannot over-redeem a coupon or corrupt a usage counter.

Accounts and access

Resend verification email (#19002). A customer who logs in with correct credentials but an unverified account now gets a one-click resend action right on the login page, instead of being sent to hunt for a separate form. There is a Shop API endpoint for it too, POST /api/v2/shop/customers/verification-request, which always returns 202 Accepted so it cannot be used to enumerate accounts.

Admin access levels (#19134). Admin users now have two independent switches, administration access and API access, available as checkboxes in the admin user form, as options in sylius:admin-user:create, and as administrationAccess and apiAccess on the Administrator API resource. An API only integration user no longer needs a way into the panel. And yes, there is a constraint that stops you from revoking your own administration access.

Translatable “account disabled” message (#19114). One less hardcoded English string in the login flow.

Payments out of the lab 🔬

Payment Request is no longer experimental (#19083, #19166). Every @experimental tag is gone from the Payment Request classes across the Component, PaymentBundle, PayumBundle and ApiBundle. It is now covered by the backward compatibility promise, which means plugin authors can build on it without bracing for changes.

Hardened payment data decryption (#19081). New allowed_classes and strict_mode options under sylius_payment.encryption let you lock down what can come out of gateway configuration and payment request payload deserialization.

A rebuilt payment methods page (#19191). The admin grid now renders each method with its gateway logo, code, gateway and status in one column instead of four.

The payment flow dropped RequestConfiguration (#19094, #19082). Payment processing interfaces take a plain Request now. One less legacy ResourceBundle concept to carry around.

Use an official PSP integration

If you use Sylius, you can help the growth of the Community Edition (Sylius-Standard)
by using an Official Payment Integration!

Cart and checkout 🛒

Post flush cart events (#19058). Four new events fire after the cart is actually written to the database: CART_ITEM_POST_ADD, CART_ITEM_POST_REMOVE, CART_POST_CHANGE, and CART_POST_CLEAR. If you push cart state to a marketing automation platform or a recommendation engine, this is the hook you have been missing.

A reusable CartItemAdder (#19139). Add-to-cart logic moved out of the Live Component into a service you can call from your own controllers and components instead of copy-pasting it.

Add to cart without a page reload (#19138). AddToCartFormComponent::addToCart() can now return null and let the Live Component re-render in place. Default behavior is unchanged, but the door is open.

Cart totals that add up (#19136). The cart summary was counting unit-level promotions twice, once inside the item subtotal and once in the discount line. A new getOrderAndItemPromotionTotal() fixes the arithmetic, and it is also exposed on the Order API resource. Heads up: the discount figure your customers see will change, because it is now the correct one.

Admin quality of life

  • Guest and registered orders are visually distinguished in the orders grid (#18995)
  • Guest customers show up in the admin customer autocomplete (#18982)
  • Navbar notifications can be links and accept translation parameters (#19015)
  • sylius:install:setup creates a country and a default zone and can assign it as the channel tax zone, so a fresh store is usable straight away (#19098)
  • Fixtures support translations for product attributes and their values (#19004)

Developer experience

Service configuration across 23 bundles moved from XML to PHP, and Behat got the same treatment: annotations converted to PHP attributes, service, page, element and suite configuration all in PHP, with behat.dist.php replacing the YAML config. That is roughly 25 pull requests of unglamorous work that makes the codebase far easier to navigate.

Also worth knowing:

  • Every Sylius validation constraint now declares explicit named arguments (#19066). Array options are deprecated. XML, YAML and attribute configuration is unaffected
  • New CatalogPricesCalculatorInterface (#19032) lets you decorate catalog display pricing independently from cart and order pricing
  • Request::get() is gone from the core in favour of explicit request bags (#19074)

Upgrading to Sylius 2.3 

The main requirement is PHP 8.3 or newer, Symfony 6.4, 7.4 or 8.

One default changed behaviour: sylius_core.order_by_identifier is now false (#18962). Set it back explicitly if you relied on it.

New deprecations: cover array options on constraints, renamed constraint message options, the behat/transliterator fallback, StringInflector::nameToSlug(), OrderRepositoryInterface::countByCustomerAndCoupon() and a handful of constructor arguments that become required. If you implement Sylius interfaces yourself, note that PromotionInterface, PromotionCouponInterface, AdminUserInterface, OrderInterface and PaymentMethodRepositoryInterface all gained methods.

The full list is in UPGRADE-2.3.md and UPGRADE-API-2.3.md.

    

Contributors of this release 🥇

2.3 is a community release in the most literal sense. Dark mode came from outside the development team, and so did the PHP grid configuration, the comparison operators, and the removal of ProxyManager.

Big kudos to mamazu, Loïc Frémont, Francis Hilaire, Simon Krull, Camille Islasse, and Dmitri Perunov, who reviewed close to thirty pull requests this cycle.

A special mention for Abderrahim Ghazali, Pascal Cescon, and Pietro Campagnano, who made their first contributions to Sylius this cycle.

And thank you to everyone on the development team who spent this cycle on the parts nobody blogs about: the CI matrix, the upmerges, and 23 bundles of configuration.

Upgrading from 2.2? Start with UPGRADE-2.3.md, then tell us how it went. Issues and pull requests are always welcome at github.com/Sylius/Sylius.

Share:
Kamil Grygierzec
More from our blog
Ecosystem News Technical 6 min read 28.09.2026
Sylius 2.3 is the first release built for Symfony 8, and it is a lot more than a dependency bump. The admin panel gets a dark theme. Every core grid is now configurable in PHP. Promotions finally behave the way multi-channel merchants actually need them to. And the Payment Request… Read More
Ecosystem 6 min read 10.09.2026
August settled into something calmer than the months before it, but no less productive: the 2.2 cycle wrapped up, Payment Request graduated to fully stable, and the AI tooling stack moved onto its new native-CLI foundation, all while the ecosystem kept new projects coming, from an AI-powered curtain shop to… Read More
Ecosystem News Sylius 6 min read 01.09.2026
Stripe has officially joined the Sylius ecosystem as our Preferred Payment Partner, marking another important step in the development of our payments ecosystem. Stripe is a financial infrastructure platform for businesses, used by millions of companies to accept payments and grow revenue.  This partnership also represents… Read More
Comments