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:
- are displayed using
the
<Wysiwyg>
component and its relatedWysiwygFragment
GraphQL fragment - get data from GraphQL fields resolved using
the
MagentoWysiwyg
type (which is the case of all default Magento rich content fields)
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.
Additional data is exposed via the GraphQL for more more advanced Customized UI components implementations.
Layout
Type | Name | Description |
---|---|---|
Row | row | Adds a row container to the stage. |
ColumnGroups | heading | Adds a column group container to the stage. |
Column | column | Adds a column to the stage. |
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
Type | Name | Description |
---|---|---|
Text | text | Adds a text container and editor to the stage. |
Heading | heading | Adds a heading container to the stage. |
Buttons | buttons | Adds a set of buttons to the stage. |
ButtonItem | button-item | Adds a individual button container to the stage. |
Divider | divider | Adds a divider container to the stage. |
HTML Code | html | Adds a HTML code container to the stage. |
Media
Type | Name | Description |
---|---|---|
Image | image | Adds a image container to the stage. |
Slider | slider | Adds a slider to the stage. |
Slide | slide | Adds a slide for the slider to the stage. |
Map | map | Adds map with locations to the stage. |
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:
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
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
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:
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
.
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.
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"
);
},
};
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.
// 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
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"
);
},
});
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 💅
Using original images
You might want to opt out of image formats, and use the original intended sizes of the images.
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.
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"
);
},
});