Appearance
Developer Overview
This section is for developers who build on Lynx: agencies, in-house teams and extension vendors. It assumes you know Magento 2 module development. For installing Lynx on a store, see Installation.
Lynx from a developer's view
Lynx keeps Magento as it is: routers, controllers, the full page cache, sessions, customer data and the admin all work as usual. What changes is how a store view that runs a Lynx theme turns a controller's result into HTML.
- Every page is an Inertia.js page. Instead of rendering blocks and templates, Magento answers with a page object: the name of a page component and its props. The first visit gets that object inside the HTML; later clicks fetch only the JSON of the next page and swap it in. There is no REST or GraphQL layer to build.
- Most pages are built from a PageBuilder layout. A layout is a tree of nodes, one per component, stored per design under the page's full action name (
catalog_product_view_type_simple,contact_index_index…). Merchants edit it in the visual editor. - Components get their data from PHP providers. Each node names a registered component. When the page is resolved, the component's provider turns the node's stored settings into props. The browser renders each node with its Vue component, loading only the components on the page.
You extend Lynx mostly by adding components (a Vue file, a declaration in etc/components/*.graphqls and an optional provider) and pages (a Magento route whose layout merchants then build).
Frontend stack
Lynx pages do not load RequireJS, KnockoutJS, jQuery or the LESS stylesheets of Magento's standard theme:
| Magento's standard theme | Lynx |
|---|---|
| KnockoutJS and jQuery | Vue 3 |
| A full page reload on every click | Inertia.js: only the next page's data is loaded, no separate API |
| RequireJS loading many small scripts | Modern JavaScript modules, built and bundled ahead of time |
| LESS compiled during deployment | Tailwind CSS 4 and daisyUI 5, driven by your design settings |
Styling uses Tailwind CSS, the utility-first CSS framework, and daisyUI, its component library for buttons, fields, cards and notices. Both read their colors, corners and sizes from the design settings of the active PageBuilder design.
Packages
Lynx is a set of Composer packages under the oocscommerce/ vendor. The oocscommerce/magento2-lynx-community metapackage requires the core set; the packages marked add-on are installed separately.
| Role | Module | Package | Purpose |
|---|---|---|---|
| Foundation | OOCS_LynxComponent | module-lynx-component | Component registry (definitions in etc/components/*.graphqls, generated Lynx props types), provider interfaces, LayoutResolver, the lynx_component cache type. Does not depend on PageBuilder. |
| Foundation | OOCS_LynxInertia | module-lynx-inertia | Inertia.js server side: InertiaService (pages, shared, deferred and optional props), JSON responses, asset versioning, SSR client, Subresource Integrity. |
| Foundation | OOCS_LynxPageBuilder | module-lynx-page-builder | Designs, layouts, the admin editor, preview and share links, theme settings, the list of editable pages. Builds the page of every layout-driven request. |
| Foundation | OOCS_LynxTheme | module-lynx-theme | Storefront glue: lynx_* layout handles, the lynx_column page layout, Page Fallback and Email Theme, the page shell (Layouts/Default), global stores, the Vue build kit (@oocs/lynx-vue) and the build commands. |
| Components | OOCS_LynxThemeComponent | module-lynx-theme-component | The default component pack: header, footer, group, row, banner, carousel, heading, cart, checkout success, contact form and more. |
| Components | OOCS_LynxCatalog | module-lynx-catalog | Product pages for every product type, category, search and advanced search; category layouts. |
| Components | OOCS_LynxProductAlert | module-lynx-product-alert | Price Alert and Stock Alert links on the product page, the stock alert unsubscribe page. Needs Magento_ProductAlert. |
| Components | OOCS_LynxCustomer | module-lynx-customer | Sign-in, registration, password pages, the customer account area, orders and returns. |
| Components | OOCS_LynxCustomerAddress | module-lynx-customer-address | The fields of every address form (checkout, address book, registration), set in Stores › Attributes › Customer Address Fields; Model\AddressFields and the client util utils/addressFields. |
| Components | OOCS_LynxGiftMessage | module-lynx-gift-message | Gift messages in the cart and on the order page. Needs Magento_GiftMessage. |
| Components | OOCS_LynxCms | module-lynx-cms | CMS pages built with Lynx, stored on the CMS page itself, with a fallback to the page's regular content. |
| Checkout | OOCS_LynxCheckout | module-lynx-checkout | The one-page checkout, with renderer pools for payment and shipping methods. |
| Checkout | OOCS_LynxCheckoutAgreements | module-lynx-checkout-agreements | Terms and Conditions on the checkout and PayPal review pages. Needs Magento_CheckoutAgreements. |
| Checkout | OOCS_LynxMSI | module-lynx-msi | Multi-Source Inventory: in-store pickup as a shipping method in the Lynx checkout, and product card stock from the website's stock. Without MSI, disable it. |
| Payments | OOCS_LynxPaymentPayPal | module-lynx-payment-paypal | PayPal payment methods and express buttons. |
| Payments | OOCS_LynxPaymentBraintree | module-lynx-payment-braintree | Braintree (add-on, needs paypal/module-braintree-core). |
| Payments | OOCS_LynxPaymentStripe | module-lynx-payment-stripe | Stripe (add-on, needs stripe/module-payments). |
| Integrations | OOCS_LynxGoogleTag | module-lynx-google-tag | GA4 as a backstage component that sends page views and ecommerce events. |
| Integrations | OOCS_LynxReCaptcha | module-lynx-re-captcha | Google reCAPTCHA for Lynx forms. |
| Integrations | OOCS_LynxNewRelic | module-lynx-new-relic | New Relic Browser on Lynx store views: prints the browser agent of Magento_NewRelicReporting. |
| Performance | OOCS_LynxCompiler | module-lynx-compiler | lynx:compile: snapshots merged module XML into generated/lynx_compile so it is not merged at runtime. |
| Performance | OOCS_LynxOptimize | module-lynx-optimize | Skips work Magento does on every request that a Lynx store view does not use. |
| AI | OOCS_LynxMcp | module-lynx-mcp | MCP server for AI clients, mainly to build PageBuilder layouts (runs on Workerman). |
Themes
| Theme | Code | Parent | What it holds |
|---|---|---|---|
| Lynx Blank | OOCS/LynxBlank | — | The parent of Lynx Vue: the root template, view.xml, logo and email styles. Not assigned to a store view. |
| Lynx Vue | OOCS/LynxVue | OOCS/LynxBlank | The Vue 3 entry point, styles and build (lynx/ folder). |
Components live in modules, under view/frontend/lynx/vue/src. A theme only overrides them. To customise the look, create a child theme of Lynx Vue: see Themes and Overrides.
Any theme whose code, or whose parent's code, starts with OOCS/Lynx counts as a Lynx theme. Everything below applies only to store views that run one; other store views behave like plain Magento.
What changes for Magento code
On a Lynx store view:
- Only
lynx_*.xmllayout files are read. Lynx adds alynx_twin of every layout handle (lynx_default,lynx_catalog_product_view…) and collects only files with that prefix, from every module and theme. Blocks declared in regular layout files of Magento or other extensions are therefore not created; blocks and templates declared inlynx_*.xmlrender as usual, in the few containers Lynx's page layout has (see Layout XML). Uselynx_<handle>.xmlfor what a Lynx page needs from layout XML, such as the title or the arguments of thelynx.inertiablock. - The page layout is always
lynx_column, which holds the Inertia mount point and two containers for scripts (lynx.after.body.start,lynx.before.body.end). - Widgets assigned through layout updates do not appear. Merchants place content with components instead.
- Translations come from files (module and theme CSV files, language packs). Translations stored in the database through Inline Translation are not used.
- Pages Lynx cannot render can go back to a standard theme. The Page Fallback setting lists full action names to render with another theme; Inertia visits to them turn into full page loads.
How a Lynx page differs
A Lynx page keeps Magento's <head>: the title, meta tags, canonical link and Open Graph tags are printed by Magento on every full page load, as on a standard store. What changes is the body. Instead of hundreds of blocks rendered from layout XML, it carries the page as JSON, one node per component, and Vue draws it. When the shopper follows a link, only the JSON of the new page's content is fetched.
Standard Magento
Every page: a full HTML document
<head>
- <title>, meta description, robots
- canonical link, Open Graph tags
<body>
header.paneltop.linksnavigationbreadcrumbssidebar.maincategory.productsproduct.infofooter_links…
Hundreds of blocks from layout XML and .phtml, plus RequireJS, KnockoutJS and jQuery widgets
Clicking a link
The next page: the whole document again
Every block is rendered and every script runs again, the screen goes blank in between
Lynx
First visit: an HTML document
<head>
- <title>, meta description, robots
- canonical link, Open Graph tags
<body>
header
[{ "name": "default_header", "props": {…} }]content
[{ "name": "default_product_listing", "props": { "products": […] } }]footer
[{ "name": "default_link_list", "props": {…} }, …]The page as JSON: one node per component, drawn by Vue. With SSR on, the server turns this JSON into HTML before sending the page
Clicking a link
The next page: JSON only
{
"component": "OOCS_LynxTheme/Pages/Default",
"props": {
"title": "Men",
"bodyClass": "catalog-category-view …",
"layout": {
"content": [{ "name": "default_product_listing", … }]
}
},
"url": "/men.html"
}Header and footer stay on screen, the content is swapped in place, the title and body class are updated
- ControllerMagento routing, as on any store
- Layout handles
lynx_*.xml: title, meta, head output - PageBuilder layoutThe design's JSON nodes for this page
- ProvidersPHP fills props: products, prices, labels
- Page objectHTML on the first visit, JSON after
- ComponentsOne Vue component per node
Request lifecycle
text
Browser ──GET /contact──▶ Magento router ──▶ controller (returns a result page)
│
layout handles: default, contact_index_index
+ lynx_default, lynx_contact_index_index
│
InertiaService::resolvePage()
│
no page declared? ──▶ PageBuilder reads the layout of the active design
(zones header, footer, backstage + content
under the path "contact_index_index")
│
LayoutResolver: each node's provider → props
│
┌─────────────────────────────────────┴───────────────────────────┐
first visit: HTML with the page JSON Inertia visit: JSON only
(rendered on the server when SSR is on) (content zone only)
└─────────────────────────────────────┬───────────────────────────┘
▼
Browser: page component OOCS_LynxTheme/Pages/Default,
DynamicNode renders one Vue component per node- Routing and controller. The request is dispatched as on any Magento store. A controller returns a regular result page; it may also declare its own page component (see Pages and Controllers).
- Layout handles. Lynx adds the
lynx_handles and forces thelynx_columnpage layout. Thelynx.inertiablock is the mount point. - Page resolution. When no controller declared a page,
OOCS_LynxPageBuilderbuilds one: the page componentOOCS_LynxTheme::Defaultwith alayoutprop. The content zone is the layout stored under the request's full action name in the active design; the other zones are the same on every page of the store view. - Providers.
LayoutResolverwalks every node, merges the component's default settings into its props and, when the component has a provider, replaces them with what the provider returns. Providers can also mark their data as deferred or optional, which then loads after the page appears (see Component Providers). - Response. A first visit gets the HTML page with the page object embedded, server-rendered when SSR is on for that route. An Inertia visit (header
X-Inertia) gets JSON. Both go through the full page cache: Inertia requests carry a_lynxquery parameter, so their JSON is cached apart from the HTML. - Rendering. The browser resolves the page component, wraps it in the page shell (
Layouts/Default) and renders each node withDynamicNode, which loads the node's component on demand.
Zones
| Zone | Where it renders | Sent |
|---|---|---|
header | Above the page content | On full page loads and partial reloads; kept by the client across visits |
content | Inside <main id="page-content"> | On every response; this is the part stored per page |
footer | Below the page content | Like header |
backstage | In a hidden container | Like header; for components that render nothing, such as trackers and scripts |
Components declare which zones they may be placed in. See Creating Components.
Working with Claude Code
Lynx has Claude Code skills for the developer tasks on these pages. With the skills in your Magento project, ask Claude Code for the result ("add a Lynx payment renderer for my method vendor_invoice with an Invoice reference field") and it writes the module the way these pages describe: global di.xml entries, a provider with translated labels, Vue files styled with the design tokens.
| Skill | Use it to |
|---|---|
basics | Shared foundation: module skeleton, dependencies, styling rules, CLI. The other skills build on it. |
component | Create a PageBuilder component and register it. See Creating Components. |
component-settings | Add or change the settings a component shows in the editor. See Setting Types. |
provider | Give a component its data from a PHP provider. See Component Providers. |
extend-component | Add markup inside a built-in component or run code at its actions. See Adding to Built-in Components. |
page | Add a storefront page or route. See Pages and Controllers. |
forms-analytics | Post forms, show server messages, refresh customer data, track analytics events. See Forms and Analytics. |
account-tab | Add a My Account tab. See My Account Tabs. |
payment-method | Add a payment method renderer to the checkout. See Payment Methods. |
shipping-method | Add a shipping method renderer to the checkout. See Shipping Methods. |
checkout-step | Add a checkout step. See Checkout Steps. |
product-type | Support a new or third-party product type. See Product Types. |
theme | Create a child theme or override a component. See Themes and Overrides. |
ssr | Make components render correctly with server-side rendering. See SSR Internals. |
testing | Write and run tests for components, stores and theme overrides. See Testing. |
extension-port | Make an existing Luma extension work on Lynx. See Luma Extension Compatibility. |
mcp-tool | Add a tool to the MCP server. See MCP Server. |
The skills come as the lynx plugin of the oocscommerce marketplace, published under the MIT License at github.com/oocscommerce/claude-skills. Install it in Claude Code 2.1.275 or later:
text
/plugin install lynx --marketplace oocscommerce/claude-skillsFor a whole team, run these from the Magento root folder and commit .claude/settings.json; each teammate then runs the install command once:
bash
claude plugin marketplace add oocscommerce/claude-skills --scope project
claude plugin install lynx@oocscommerce --scope projectDescribe what you want and Claude Code picks the matching skill, or call one by name, such as /lynx:component. To update, run claude plugin marketplace update oocscommerce and claude plugin update lynx@oocscommerce, or turn on auto-update in /plugin › Marketplaces.
Review what it writes like any other code, then build and test as described in Development Setup. Connected to your store's MCP server as well, the same session can place the new component on a page and check it in the preview.
Where to go next
| Page | Read it to |
|---|---|
| Development Setup | Install Lynx for development, build the frontend, use the lynx:* commands |
| Differences from Standard Magento | What Magento does differently on a Lynx store: layout XML, widgets, translations, checkout |
| Layout XML | The lynx_ prefix, the page structure, what blocks and layout files are still for |
| Moving from Luma or Hyvä with an LLM | Rebuild an existing store's design on Lynx with an AI assistant over MCP, and have missing components made |
| Making a Luma Extension Compatible | Port an extension's storefront to Lynx in a compatibility module, with a worked example |
| My Account Tabs | Add a tab to the customer account: sidebar entry, section component, editor page |
| Pages and Controllers | Add a storefront page, use props, deferred props, navigation and forms |
| Creating Components | Write a Vue component and register it for the editor |
| Setting Types | Declare the settings merchants fill in for a component |
| Component Providers | Turn settings into props in PHP, defer data, provide sample data |
| Product Types | One product page design per type, and how to support a new product type |
| Frontend Code | Imports, stores, styling with Tailwind CSS and daisyUI |
| Themes and Overrides | Create a child theme and override components |
| Payment Methods | Add a payment method to the Lynx checkout |
| Shipping Methods | Add a renderer under a shipping rate |
| Checkout Steps | Add your own step to the Lynx checkout, or change the built-in ones |
| Forms and Analytics | reCAPTCHA on forms, analytics events |
| Server-side Rendering | How SSR runs and what components must respect |
| MCP Server | Run the MCP server and add tools |