Skip to content

Differences from Standard Magento ​

Lynx runs inside Magento: the same controllers, models, observers, plugins, APIs and admin. What changes is how a storefront page is built and sent to the browser, plus a few runtime shortcuts that come with it. This page lists what behaves differently on a Lynx store, so you know what your extension can rely on.

When a request counts as Lynx ​

A request is a Lynx request when the design theme of the store view is a Lynx theme, or a child theme of one (Content › Design › Configuration). Everything on this page applies only then. Store views on another theme, the admin, cron, CLI, REST and GraphQL behave like standard Magento, except for the compiled configuration, which covers every area.

Two settings narrow it down:

  • Page Fallback (Stores › Configuration › Lynx › Theme Settings › Page Fallback) renders the full action names you list, such as shipping_tracking_popup or multishipping_*, with a standard Magento theme. Those pages are not Lynx pages and get none of the differences below. When the Lynx storefront navigates to one of them, it switches to a full page load.
  • Render with Lynx on a CMS page or category does not change the theme. It chooses what fills the content area of that page: its own PageBuilder layout when on, the standard content when off. See Content.

Page rendering ​

A Lynx page is a single-page app. The first visit returns HTML with the page data embedded as JSON. Following a link afterwards fetches only that JSON (an Inertia request, sent with the X-Inertia header) and the client swaps the page without a reload. The page itself is a PageBuilder layout: header, footer and a content area chosen by the controller's full action name.

Magento featureOn Lynx storesWhat to do instead
RequireJS, KnockoutJS, UI components, jQuery and jQuery UI widgets, x-magento-init, data-mage-initNot loaded on the storefrontBuild a Lynx component
Layout XML of modules and themes (default.xml, catalog_product_view.xml…)Not collected. Only files named lynx_*.xml are read, and every handle also gets a lynx_ twin (catalog_product_view → lynx_catalog_product_view)Put server-side layout needs (head metadata, block arguments) in lynx_<handle>.xml; put visible content in a component
Blocks and .phtml templatesRender as usual when declared in a lynx_*.xml file, inside the few containers Lynx's page layout has (head.additional, lynx.after.body.start, lynx.main.container, lynx.before.body.end). Blocks declared only in regular layout files are never created, so plugins on them and their view models do not run. Lynx itself declares very few blocksDeclare the block in lynx_<handle>.xml; for content merchants place or that changes as shoppers navigate, a component with a PHP provider. See Layout XML
Page layouts (1column, 2columns-left…) and the Layout field of pages, categories and productsIgnored: every page uses Lynx's own page layoutArrange the page in PageBuilder
<head> assets added by layout XML (<css>, <script>, <link>)Only those declared in lynx_*.xml filesLoad scripts from a component when it mounts
Head output on later navigationsOnly the full page load renders <head>. Inertia requests update the document title and the <body> class; other head tags stay as they wereKeep per-page data in page props, not in <head>
Magento's LESS stylesheetsNot used; Lynx ships Tailwind CSS styles built from the design settingsStyle components with the theme's tokens
Pages of other modules (/blog/post/view, /brand/index/index…)The controller runs, but only the header, footer and the PageBuilder layout saved for that action name show; blocks from its regular layout files do not (those from a lynx_ file of the module do)Return an Inertia page from the controller, or list the route in Page Fallback

What still runs on every Lynx request: routing and controllers, controller_action_* and other events and observers, plugins on models and services, page config set by controllers and lynx_*.xml files (title, meta tags, <html> and <body> attributes), the full page cache and its cache tags, sessions, form keys and flash messages. Lynx keeps Magento's title, meta and canonical for catalog pages by declaring the core blocks that set them in its own lynx_*.xml files, rendering nothing. If your extension sets head data from a block's _prepareLayout(), declare that block the same way.

CMS content and widgets ​

Magento featureOn Lynx storesWhat to do instead
Content › Widgets instances placed on pages or containers (widget layout updates)Not shown. The stored layout updates are skipped when the layout is mergedAdd the content with a PageBuilder component
CMS page Content fieldShown in the content area when Render with Lynx is off, or when the page has no Lynx layout. Directives (, , , ) are expanded on the server and the HTML is inserted as isScripts inside that HTML do not run; use a component for anything interactive
CMS page Layout Update XML and custom layout update filesNot applied, as with other layout XMLDesign the page in PageBuilder
Category Display Mode and Add CMS BlockNot rendered; the category page shows its PageBuilder layoutTurn on Render with Lynx for that category and design its content
Product and category descriptionsRendered through Magento's output helper, so directives work; the HTML is inserted without running scriptsSame as CMS content
/ placeholders inside layout XMLLeft as literal textBuild URLs in PHP

Customer data sections ​

Private content still comes from customer/section/load and the sections declared in sections.xml. Your SectionSourceInterface classes keep working. Differences on the client:

  • Storage. Lynx keeps sections in IndexedDB (database lynx_customer_data), not in the mage-cache-storage localStorage keys. Scripts that read localStorage directly find nothing.
  • Invalidation. There is no global AJAX listener. A Lynx component that posts to an action gets that action's section list from the server (resolved from sections.xml, exact route keys only, no wildcard matching) and reloads those sections itself through the customer data store. Your component does the same after its own requests.
  • Wishlist section. On Lynx stores the wishlist section returns the counter and count but an empty items list, since no block is rendered for it.

Translations ​

Magento featureOn Lynx storesWhat to do instead
Database translations (the translation table)Not appliedUse i18n/<locale>.csv in a module or theme, or a language pack
Inline Translation (Developer › Translate Inline)Has no effect, because it writes to the database; leave it off for Lynx store views, or text shows its translation markersSame as above
JavaScript translation dictionary (js-translation.json, $t(), mage/translate)Not loaded; components receive text already translated on the server with __()Translate labels in your provider or controller, see Translations: on the server only
Text entered in PageBuilderTranslatable per store view in the editor (text, textarea and rich text settings), then passed through __()Translate in the editor, or add the default string to a CSV

When lynx:compile has been run, the translation dictionary of each Lynx theme and locale is read from the compiled files, so a CSV change shows only after bin/magento lynx:compile translate.

Compiled configuration ​

OOCS_LynxCompiler merges Magento's XML configuration once at deployment instead of after every cache flush:

bash
bin/magento lynx:compile                  # every compiler
bin/magento lynx:compile events routes    # only some of them

It covers most of the XML Magento merges at runtime, such as events.xml, routes.xml, config.xml, view.xml, sections.xml, webapi.xml, extension_attributes.xml, csp_whitelist.xml, ACL, menu, indexer and message queue files, plus the Lynx translation dictionaries. di.xml is not included: setup:di:compile already handles it. The result lives in generated/lynx_compile and is read in every area and on every store, not only Lynx store views.

The compiled files are a snapshot:

  • setup:upgrade, module:enable and module:disable delete them together with generated/code, so run lynx:compile again afterwards.
  • Changing any covered XML file by hand, including in a third-party module, needs a new lynx:compile. Until then the store keeps using the old configuration without any warning. cache:flush does not clear the compiled files.
  • Without compiled files, Magento merges configuration as usual, only slower.
  • To remove them, use System › Cache Management › Flush Lynx Compile, or delete generated/lynx_compile.

Recommended deployment order: setup:upgrade → setup:di:compile → lynx:compile → static content deploy.

Checkout ​

On a Lynx store, checkout/index/index shows the Lynx checkout instead of Magento's KnockoutJS checkout. The controller and quote APIs are Magento's own; the steps, address form, shipping methods and payment methods are Lynx components.

  • Offline payment methods (check / money order, bank transfer, cash on delivery…) show without extra work. Online (gateway) methods are hidden until they have a Lynx renderer. See Payment Methods.
  • A shipping method that needs extra input, such as a pickup location, needs a Lynx renderer. See Shipping Methods.
  • Checkout layoutProcessor plugins, checkout_index_index.xml changes and checkoutConfig JavaScript mixins do not apply.
  • The checkout always opens as a full page load, never as an in-app navigation.
  • Checkout with multiple addresses is in the default Page Fallback list, so it renders with a standard theme once Page Fallback is on.

Other runtime changes ​

These skip work the Lynx storefront does not need. Most have no visible effect, but an extension can notice them:

  • Layout cache per theme. The merged layout is cached per Lynx theme rather than per theme and store view.
  • CSP headers. Inertia (JSON) responses carry no Content Security Policy header; the full page load does.
  • RTL. Store views with an Arabic, Persian or Hebrew locale get dir="rtl" on <html>.
  • UI component factory. Created lazily on the storefront, REST and GraphQL, since Lynx pages declare no UI components.
  • Persistent cart check. The per-request persistent quote check returns early when there is no quote or the customer is signed in, and calls Magento's own observer only for a persistent quote.

Making your extension work on Lynx ​

Your extension hasOn Lynx
Observers, plugins, models, cron, admin pages, REST and GraphQL endpointsWork unchanged
Storefront UI in layout XML, .phtml, KnockoutJS or jQuery widgetsRebuild it as a Lynx component with a PHP provider, so merchants place it in PageBuilder
A storefront page (controller with its own layout)Return an Inertia page from the controller, or list its route in Page Fallback
Forms posting to your controllersSubmit with an Inertia form or fetch, then reload the customer data sections the action invalidates (see Forms)
Tracking or analytics scriptsListen to the analytics bus instead of DOM events or KnockoutJS observables (see Forms)
Translations in the database or through Inline TranslationMove them to i18n/<locale>.csv or a language pack
Widget instances or CMS blocks placed through layout updatesOffer a PageBuilder component instead
Payment or shipping method with its own checkout UIAdd a payment renderer or shipping renderer
New or changed XML configurationRun bin/magento lynx:compile after installing or updating

For a complete walkthrough with a worked example, see Making a Luma Extension Compatible.