Skip to content

Layout XML ​

Lynx pages still go through Magento's layout system: handles, layout files, page config, blocks. What changes is which files are read and what the layout is used for. On a Lynx page the visible content is built from components, so layout XML keeps the jobs around it: the <head>, page config, cache behavior and the mount point.

Everything on this page applies only to store views that run a Lynx theme or a child of one.

The lynx_ prefix ​

Two things happen on every Lynx request:

  1. Only files named lynx_*.xml are collected. Magento normally collects every *.xml in the layout/ folders of modules and themes. On a Lynx store view the collector looks for lynx_*.xml only, so default.xml, catalog_product_view.xml and the layout files of other extensions are not read at all.
  2. Every handle gets a lynx_ twin. Before the layout loads, Lynx adds lynx_<handle> next to each handle of the request. A product page with the handles default, catalog_product_view and catalog_product_view_type_simple also gets lynx_default, lynx_catalog_product_view and lynx_catalog_product_view_type_simple.

So a file applies to the same pages as its standard counterpart, once you add the prefix:

Standard Magento fileOn a Lynx store view
default.xmllynx_default.xml
catalog_product_view.xmllynx_catalog_product_view.xml
catalog_category_view.xmllynx_catalog_category_view.xml
cms_index_index.xmllynx_cms_index_index.xml
customer_account_index.xmllynx_customer_account_index.xml
vendor_faq_index_index.xml (your route)lynx_vendor_faq_index_index.xml

Files go in the usual places: view/frontend/layout/ of a module, <Vendor_Module>/layout/ of a theme. Theme files merge with module files and override them through layout/override/ exactly as in standard Magento.

The page layout is always lynx_column, set by Lynx before the layout loads, whatever the page or the files say.

Do not set a page layout in a lynx_ file

Leave out the layout="…" attribute of <page>. A page layout declared in a layout file is still picked up when Lynx answers an Inertia visit, and the visit then gets a full HTML page instead of JSON. After removing such an attribute, clean the layout cache: it keeps the merged files.

The page structure ​

lynx_column and OOCS_LynxTheme's lynx_default.xml give every Lynx page this skeleton:

text
<head>                         title, meta, links from page config and lynx_*.xml
  head.additional              block for extra <head> output (Open Graph…)
<body id="html-body">
  lynx.after.body.start        store config for the client, notices
  lynx.page.wrapper
    lynx.main.container
      lynx.inertia             the mount point: page JSON, SSR markup when on
  lynx.before.body.end         scripts at the end of the body

The storefront itself (header, content, footer) is drawn by the page component inside lynx.inertia. There is no page.wrapper, main, sidebar.main, content or footer container to reference: those belong to Magento's standard page layouts.

Blocks and templates ​

Blocks and .phtml templates declared in a lynx_*.xml file work as in standard Magento: they are created, their view models and plugins run, and their HTML is printed in the container you put them in. The difference is how little there is around them: Lynx's page layout has only the containers above, and Lynx declares very few blocks of its own (the store config, notices, the mount point, a handful of headless blocks for meta and cache tags). The storefront you see is the page component, not a tree of blocks.

What to keep in mind when you add one:

  • Pick a Lynx container. head.additional, lynx.after.body.start, lynx.main.container (next to the mount point) or lynx.before.body.end. Containers of Magento's standard page layouts (content, sidebar.main, product.info.main…) do not exist.
  • Its HTML sits outside the app. It is printed on a full page load and stays in the page while the shopper navigates; the page component around it is redrawn, the block is not.
  • It is not re-rendered on Inertia visits. The layout of the new page is still built, so _prepareLayout() runs, but only the output of lynx.inertia is sent.

So a block suits markup that is the same on every page or only matters on the first load (a verification tag, a noscript fallback, a script loader). Content that differs per page, that merchants place, or that must update as shoppers navigate belongs in a component.

What layout XML is for on Lynx ​

Page title and meta ​

Set the title, meta and robots of a page as usual. They are printed in <head> on a full page load, and the title travels with the JSON of an Inertia visit too.

xml
<!-- view/frontend/layout/lynx_vendor_faq_index_index.xml -->
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <head>
        <title>Frequently Asked Questions</title>
        <meta name="description" content="Answers about orders, shipping and returns"/>
        <meta name="robots" content="INDEX,FOLLOW"/>
    </head>
</page>

Titles and meta set from PHP (a controller calling $page->getConfig()->getTitle()->set(), an observer, a block's _prepareLayout()) work the same way.

Headless blocks that set page config ​

When the meta of a page comes from an entity, add a block that renders nothing and fills the page config in _prepareLayout(). Lynx does this for categories: lynx_catalog_category_view.xml adds OOCS\LynxCatalog\Block\CategoryMeta to lynx.main.container, which sets the title, description, keywords and canonical link of the category. The same pattern adds full page cache tags (OOCS\LynxCatalog\Block\ProductCacheTags on product pages).

xml
<referenceContainer name="lynx.main.container">
    <!-- Renders no HTML; sets title/meta/canonical in _prepareLayout() -->
    <block class="Vendor\Faq\Block\QuestionMeta" name="vendor.faq.question.meta"/>
</referenceContainer>

The block's _toHtml() returns an empty string. Layout generation still runs on Inertia visits, so what it sets in the page config reaches the JSON.

Extra <head> output ​

Add a template block to head.additional for markup the page config cannot express, as Lynx does for the product Open Graph tags in lynx_catalog_product_view.xml:

xml
<referenceBlock name="head.additional">
    <block class="Magento\Framework\View\Element\Template" name="vendor.faq.schema"
           template="Vendor_Faq::head/schema.phtml"/>
</referenceBlock>

<head> attributes (<attribute name="prefix" value="og: …"/>), <css>, <link> and <script> entries also work.

Scripts and markup outside the app ​

lynx.after.body.start and lynx.before.body.end take blocks that print markup at the start or end of <body>, such as a tag manager snippet. Prefer a component in the Backstage zone for tracking (see Forms and Analytics): it is placed per design in the editor and follows client-side navigation.

Page behavior ​

  • Keep a page out of the full page cache: <update handle="lynx_no_cacheable"/>. Lynx uses it for the checkout and the PayPal review page.
  • Arguments of the mount point (ssr, encrypt_history, clear_history, sri): set them on the lynx.inertia block. See Pages and Controllers.

What does not work ​

In layout XMLOn a Lynx pageInstead
Blocks placed in containers of standard page layouts (content, sidebar.main, product.info.main…)Those containers do not exist in lynx_columnOne of the Lynx containers, or a component placed in the editor
<referenceBlock> / <move> on Luma blocks (product.info.price, catalog.leftnav…)Those blocks are never declaredA component, or a theme override of the Lynx component
<head> assets that one page needs (<script src> in lynx_catalog_product_view.xml)Loaded only when that page is the first one opened; a visit from another page does not load themImport the code from the component that needs it
Widgets placed through Content › Widgets layout updatesSkippedA component
Blocks declared only in regular layout files, and plugins or observers on their output (toHtml, *_html_after)The files are not read, so the blocks are never created and those hooks never runDeclare the block in a lynx_ file, or hook into the data the component's provider reads

On Inertia visits ​

When the shopper moves between pages, Lynx still builds the layout of the new page, so handles, page config and _prepareLayout() run as usual. Before rendering it removes every block except lynx.inertia and answers with JSON. Of the <head>, only the title and the body classes are sent with that JSON and applied in the browser. Meta tags, canonical links and Open Graph tags are printed on full page loads, which is what search engines and link previews receive.

Adapting an extension ​

An extension that adds head markup or a script through default.xml works on Lynx once the same instructions are in lynx_default.xml:

xml
<!-- Vendor_Module: view/frontend/layout/lynx_default.xml -->
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="head.additional">
            <block class="Vendor\Module\Block\Verification" name="vendor.module.verification"
                   template="Vendor_Module::verification.phtml"/>
        </referenceBlock>
    </body>
</page>

Copy only what belongs in <head>, the page config or the body edges. Anything the extension renders inside the page becomes a component. For the rest of the differences, see Differences from Standard Magento.

After adding or changing a lynx_*.xml file, run bin/magento cache:clean layout full_page.