Skip to main content
Version: next

Use and extend the Page Builder

Since version 3.4

Adobe Commerce and Magento 2.4.3+ allow merchants to author pages using Page Builder. Front-Commerce supports Page Builder-managed content out of the box. In this guide, you will learn how to use this feature and extend it.

Common tasks as a developer would consist in creating new content types and refining UI components so that merchants can create the rich shopping experiences that were designed for their customers.

Prerequisites

Page Builder is only available for content that:

Please check these prerequisites first if your content does not appear properly.

Concepts

Page Builder content types have 2 integration points:

  • server side data conversion will parse Magento HTML response to extract rich structured data exposed in GraphQL
  • client side React components will display the content using existing components, from data fetched from GraphQL

Supported content types

We currently support these content types in a basic way.

info

Additional data is exposed via the GraphQL for more more advanced Customized UI components implementations.

Layout

Magento Page Builder Layout

TypeNameDescription
RowrowAdds a row container to the stage.
ColumnGroupsheadingAdds a column group container to the stage.
ColumncolumnAdds a column to the stage.
We only have partial support for the row content type.

Unsupported props:

  • ❌ Mobile Image
  • ❌ Fluid Width - requires a supported layout

  • ❌ Full Bleed - requires a supported layout

  • ❌ Video Background - data exposed with video

  • ❌ Parallax Background - data exposed with parallax

Elements

Magento Page Builder Elements

TypeNameDescription
TexttextAdds a text container and editor to the stage.
HeadingheadingAdds a heading container to the stage.
ButtonsbuttonsAdds a set of buttons to the stage.
ButtonItembutton-itemAdds a individual button container to the stage.
DividerdividerAdds a divider container to the stage.
HTML CodehtmlAdds a HTML code container to the stage.

Media

Magento Page Builder Media

TypeNameDescription
ImageimageAdds a image container to the stage.
SlidersliderAdds a slider to the stage.
SlideslideAdds a slide for the slider to the stage.
MapmapAdds map with locations to the stage.
WIP

Support for internal product, page and category links for button-item and image

Upcoming

  • Internal links.
  • Native Magento widgets.

Let us know if you have specific needs.

Styles

Front-Commerce supports custom styles from the Advanced Magento settings of all content types:

Advanced styles Magento settings

Extend the Page Builder

You can extend existing Page Builder content types, or register new ones specific to your projects. To do so, there are 2 extension points: UI and GraphQL data resolution.

Customize UI components

WIP

If you need details right now, please contact us. We will make sure to answer you in a timely manner.

  • override theme/modules/Wysiwyg/MagentoWysiwyg/PageBuilder/_appComponents.scss to register your custom styles
  • override theme/modules/Wysiwyg/MagentoWysiwyg/PageBuilder/appComponentsMap.js to register new components (or override existing ones)

Expose content types data in GraphQL

WIP

If you need details right now, please contact us. We will make sure to answer you in a timely manner.

The PageBuilder loader allows you to register new content types.

First, you must define your content type. Content types must extend the ContentType class (see below). The name should match Magento's content type identifier and the extractData method can be used to return structured data to be exposed as GraphQL fields for this type.

import { ContentType } from "@front-commerce/magento2/wysiwyg";

export default class Foo extends ContentType {
name = "foo";
extractData(node) {
return {
// custom data. Can also be extracted from the passed `node` information
bar: "baz",
};
}
}

Then you must register it from a contextEnhancer, using the PageBuilder.registerContentType method, along with a resolver for the MyPageBuilderFooData graphql field:

extension/acme-extension/modules/wysiwyg/runtime.ts
import { createGraphQLRuntime } from "@front-commerce/core/graphql";

export default createGraphQLRuntime({
resolvers: {
MyPageBuilderFooData: new PageBuilderContentTypeResolver(),
},
contextEnhancer: ({ loaders }) => {
PageBuilder.registerContentType(
new Foo(), // <-- the content type defined above
"MyPageBuilderFooData" // <-- the GraphQL type for related data default to MagentoPageBuilderDefaultData (if no additional data)
);
},
});

If your content type exposes additional data with a specific GraphQL type (MyPageBuilderFooData in this current example), you will then have to update your resolvers. To do so, Front-Commerce provides a generic PageBuilderContentTypeResolver class that will expose data returned by the content type's extractData.

extension/acme-extension/modules/wysiwyg/index.ts
import PageBuilderContentTypeResolver from "@front-commerce/magento2/wysiwyg";
import { createGraphQLModule } from "@front-commerce/core/graphql";

export default createGraphQLModule({
namespace: "ACME/Wysiwyg",
loadRuntime: () => import("./runtime"),
dependencies: [
"Magento2/Wysiwyg", // ensure that Widget related features are available
"Magento2/Catalog/Product", // ensure that you can fetch a product in your Wysiwyg data
],
typeDefs: /* GraphQL */ `
# Your custom page builder node data must implement
# both MagentoPageBuilderNodeData and WysiwygNodeData GraphQL interfaces
type MyPageBuilderFooData implements MagentoPageBuilderNodeData & WysiwygNodeData {
dataId: ID
appearance: String
bar: String # <- custom structured data
}
`,
});

Extend the Image content-type

The image content-type uses defaults for the formatting to optimise performance,

  • Desktop: large
  • Mobile: medium

This might be limiting for content creators as they might want to display dynamically sized images, or use a different format.

You can register your own image content type, and use a custom format, or you can determine the format dynamically using a class.

Using a custom format

This is much simpler as we only need to register an Image content-type with the PageBuilder loader.

extensions/acme-extension/modules/page-builder/loaders/index.js
import { Image } from "@front-commerce/magento2/wysiwyg"; // We import the orignal image as it already extracts all the relevant data

// The formats are the keys of the presets defined
// inside your src/config/images.js file.
const DESKTOP_IMAGE_FORMAT = "custom_desktop_format";
const MOBILE_IMAGE_FORMAT = "custom_mobile_format";

export default {
dependencies: ["Magento2/Wysiwyg"],
contextEnhancer: ({ loaders }) => {
const { PageBuilder } = loaders;
PageBuilder.registerContentType(
new Image(DESKTOP_IMAGE_FORMAT, MOBILE_IMAGE_FORMAT),
"MagentoPageBuilderImageData"
);
},
};
http://localhost:4000/default-image-format

Default Formats

Determine format dynamically

With this method you can use the classes from the page builder to determine the format, this will allow you to still take advantage of optimised images.

First let's create our DynamicImage content-type which contains the logic to determine the format.

extensions/acme-extension/modules/page-builder/loaders/content-types/DynamicImage.js
// We will extend the original Image content-type
import { Image } from "@front-commerce/magento2/wysiwyg";
import imageConfig from "~/config/images";

const presetKeys = Object.keys(imageConfig.presets);

const getPresetFormat = (startsWith, classes = "") => {
return presetKeys.find(
(preset) =>
classes
.split(" ")
.filter((className) => className.startsWith(startsWith)) // only target specific class names starting with formt__
.some((className) => className.endsWith(preset)) // check if the preset is present in the class name
);
};

export default class DynamicImage extends Image {
extractData = (node) => {
// First we extact the original image data (required for the graphql)
const originalImageData = super.extractData(node);

// We then get the classes attached to the image node
const classes = super.findAttributeValue(node, "class", "");

// We can then use the classes to determine the format
const desktopFormat = getPresetFormat("format__desktop__", classes);
const mobileFormat = getPresetFormat("format__mobile__", classes);

if (desktopFormat) {
originalImageData.desktopFormat = desktopFormat;
}
if (mobileFormat) {
originalImageData.mobileFormat = mobileFormat;
}

return originalImageData;
};
}

We then need to register the DynamicImage content-type with the PageBuilder

extensions/acme-extension/modules/page-builder/runtime.ts
import DynamicImage from "./content-types/DynamicImage";
import { createGraphQLRuntime } from "@front-commerce/core/graphql";

export default createGraphQLRuntime({
contextEnhancer: ({ loaders }) => {
const { PageBuilder } = loaders;
PageBuilder.registerContentType(
new DynamicImage(),
"MagentoPageBuilderImageData"
);
},
});
tip

The DynamicImage extends the original Image content-type so you can pass default format params, which will be used when a format class does not exists, as per the previous example: Using a custom format

Congratulations! 🥳 You now have a fully functional Dynamic PageBuilder Image 💅

http://localhost:4000/default-dynaimc-format

Dynamic Formats

Using original images

You might want to opt out of image formats, and use the original intended sizes of the images.

danger

This will have a performance impact as it will no longer use the image proxy service which optimises images.

To do so, you can follow the Using a custom format and supply the original format.

extensions/acme-extension/modules/page-builder/loaders/runtime.ts
import { Image } from "@front-commerce/magento2/wysiwyg";
import { originalPresetCode } from "~/config/images";
import { createGraphQLRuntime } from "@front-commerce/core/graphql";

export default createGraphQLRuntime({
contextEnhancer: ({ loaders }) => {
const presetCode = originalPresetCode || "original";
const { PageBuilder } = loaders;
PageBuilder.registerContentType(
new Image(presetCode, presetCode),
"MagentoPageBuilderImageData"
);
},
});
http://localhost:4000/original-format

Original Format