Skip to main content

Frozen Header & Footer

Easy Email Pro provides two approaches to create fixed header and footer elements to meet different business requirements:

  1. Fully Frozen Approach: Using headerElement and footerElement configuration, users cannot modify or delete them at all
  2. Partially Editable Approach: Using blocks with PAGE_HEADER and PAGE_FOOTER category, fixed at the top and bottom, cannot be dragged or deleted, but allows partial content modification (such as text and background color)

Suitable for scenarios that require complete content locking, such as watermarks that can only be removed after user subscription, or required subscription links that must be retained.

Configuration

Configure frozen blocks in your editor setup:

import { Element } from "easy-email-pro-core";

// Define header element
const headerElement: Element = {
type: "standard-section",
data: {},
attributes: {
"background-repeat": "no-repeat",
"background-color": "#4a90e2",
},
children: [
{
type: "standard-column",
attributes: {
width: "100%",
},
data: {},
children: [
{
name: "Text",
type: "standard-paragraph",
data: {},
attributes: {},
children: [
{
text: "FROZEN HEADER",
},
],
},
],
},
],
};

// Define footer element
const footerElement: Element = {
type: "standard-section",
data: {},
attributes: {
"background-repeat": "no-repeat",
"background-color": "#4a90e2",
},
children: [
{
type: "standard-column",
attributes: {
width: "100%",
},
data: {},
children: [
{
name: "Text",
type: "standard-paragraph",
data: {},
attributes: {},
children: [
{
text: "FROZEN FOOTER",
},
],
},
],
},
],
};

// Apply to editor
export default function MyEditor() {
const config = Retro.useCreateConfig({
// ... other config options
headerElement: headerElement,
footerElement: footerElement,
});

return (
<EmailEditorProvider {...config}>
<EditorHeader />
<Layout.Content>
<Retro.Layout />
</Layout.Content>
</EmailEditorProvider>
);
}

Server-side Usage

When generating the final email HTML on the server, include the frozen elements:

const emailHtml = EditorCore.toMJML({
element: pageElement as PageElement,
mode: "production",
mergetagsData: {},
headerElement: headerElement,
footerElement: footerElement,
});

Features

  • Fully frozen, users cannot modify or delete
  • Displayed in both edit and preview modes
  • Must include the same elements when generating the final email HTML
  • Supports all standard block attributes and styles

Suitable for scenarios that require fixed positions but allow partial modifications, for example:

  • Footer has required subscription links that must be retained, but allows modification of text and background color
  • Header has a fixed navigation structure, but allows style adjustments

Default Implementation

Easy Email Pro has built-in basic PageHeader and PageFooter blocks that can be used directly in the editor. These blocks are configured by default with PAGE_HEADER and PAGE_FOOTER category, and will automatically apply restrictions for fixed position, drag prevention, and deletion prevention.

Using in Templates

In the email template JSON data, you can directly use blocks of type page-header and page-footer:

{
"type": "page",
"children": [
{
"type": "page-header",
"data": {
"content": [
{
"type": "standard-section",
"children": [
{
"type": "standard-column",
"children": [
{
"type": "standard-paragraph",
"children": [{ "text": "This is fixed header content" }]
}
]
}
]
}
],
"editable": true
},
"attributes": {
"background-color": "#f0f0f0",
"padding-top": "20px",
"padding-bottom": "20px"
}
},
{
"type": "standard-section",
"children": [
// Main email content
]
},
{
"type": "page-footer",
"data": {
"content": [
{
"type": "standard-section",
"children": [
{
"type": "standard-column",
"children": [
{
"type": "standard-paragraph",
"children": [{ "text": "This is fixed footer content" }]
}
]
}
]
}
],
"editable": true
},
"attributes": {
"background-color": "#f0f0f0",
"padding-top": "20px",
"padding-bottom": "20px"
}
}
]
}

Editor Behavior

  • These blocks will automatically appear in the header or footer area of the editor
  • Users cannot drag to move their positions
  • Operation tools such as delete buttons will be hidden
  • Editable Property: The editable property in data controls whether the content within the block can be edited
    • When editable: true: Users can trigger editing interactions and modify content within the blocks (such as text, background color, and other style attributes)
    • When editable: false: Content editing is disabled, and users cannot modify the content within the blocks
  • Child elements within the blocks can be edited normally when editable: true

Default Configuration Panel

The editor provides default configuration panels (ConfigurationPanel) for PageHeader and PageFooter to help users quickly configure common properties:

PageHeader Default Configuration Items:

  • Logo image URL
  • Logo width
  • Background color

PageFooter Default Configuration Items:

  • Text content
  • Background color

These configuration panels are implemented using the Schema approach and will automatically map to the corresponding data structure paths.

Custom Configuration Panel

If the default configuration panel cannot meet your needs, you can customize the configuration panel by overriding BlockSchemasMap or ConfigPanelsMap.

The Schema approach is simpler and suitable for most scenarios. Define the configuration panel by overriding BlockSchemasMap:

import { BlockSchemasMap } from "easy-email-pro-theme";
import { ElementType } from "easy-email-pro-core";
import { BlockSchema } from "@easy-email-pro-theme/typings";
import { t } from "easy-email-pro-core";

// Custom PageHeader configuration panel Schema
const CustomPageHeaderSchema: BlockSchema = [
{
type: "CollapseGroup",
name: "0",
header: t("Header Settings"),
defaultActive: true,
children: [
{
type: "ImageUrl",
name: "data.content.0.children.0.children.0.attributes.src",
label: t("Logo"),
},
{
type: "PixelField",
name: "data.content.0.children.0.children.0.attributes.width",
label: t("Logo Width"),
},
{
type: "BackgroundColor",
name: "attributes.background-color",
label: t("Background color"),
},
// Add more custom configuration items...
],
},
];

// Custom PageFooter configuration panel Schema
const CustomPageFooterSchema: BlockSchema = [
{
type: "CollapseGroup",
name: "0",
header: t("Footer Settings"),
defaultActive: true,
children: [
{
type: "TextField",
name: "data.content.0.children.0.children.0.children.0.text",
label: t("Footer Text"),
},
{
type: "BackgroundColor",
name: "attributes.background-color",
label: t("Background color"),
},
{
type: "TextField",
name: "attributes.padding-top",
label: t("Padding Top"),
},
{
type: "TextField",
name: "attributes.padding-bottom",
label: t("Padding Bottom"),
},
// Add more custom configuration items...
],
},
];

// Override the default configuration panel
BlockSchemasMap[ElementType.PAGE_HEADER] = CustomPageHeaderSchema;
BlockSchemasMap[ElementType.PAGE_FOOTER] = CustomPageFooterSchema;

Method 2: Using React Component Approach

If you need more complex interaction logic, you can use the React component approach. Define the configuration panel by overriding ConfigPanelsMap:

import { ConfigPanelsMap } from "easy-email-pro-theme";
import { ElementType } from "easy-email-pro-core";
import { Path } from "slate";
import React from "react";
import {
AttributesPanelWrapper,
CollapseWrapper,
AttributeField,
} from "easy-email-pro-theme";
import { useSelectedNode } from "easy-email-pro-editor";
import { t } from "easy-email-pro-core";
import { Collapse } from "@arco-design/web-react";

// Custom PageHeader configuration panel component
const CustomPageHeaderPanel = ({ nodePath }: { nodePath: Path }) => {
const { selectedNode } = useSelectedNode();

if (!selectedNode) return null;

return (
<AttributesPanelWrapper>
<CollapseWrapper defaultActiveKey={["0", "1"]}>
<Collapse.Item name="0" header={t("Logo Settings")}>
<AttributeField.ImageUrl
label={t("Logo URL")}
name="data.content.0.children.0.children.0.attributes.src"
path={nodePath}
/>
<AttributeField.PixelField
label={t("Logo Width")}
name="data.content.0.children.0.children.0.attributes.width"
path={nodePath}
/>
</Collapse.Item>
<Collapse.Item name="1" header={t("Style Settings")}>
<AttributeField.BackgroundColor
label={t("Background color")}
name="attributes.background-color"
path={nodePath}
/>
<AttributeField.TextField
label={t("Padding Top")}
name="attributes.padding-top"
path={nodePath}
/>
<AttributeField.TextField
label={t("Padding Bottom")}
name="attributes.padding-bottom"
path={nodePath}
/>
</Collapse.Item>
</CollapseWrapper>
</AttributesPanelWrapper>
);
};

// Custom PageFooter configuration panel component
const CustomPageFooterPanel = ({ nodePath }: { nodePath: Path }) => {
const { selectedNode } = useSelectedNode();

if (!selectedNode) return null;

return (
<AttributesPanelWrapper>
<CollapseWrapper defaultActiveKey={["0"]}>
<Collapse.Item name="0" header={t("Footer Content")}>
<AttributeField.TextField
label={t("Footer Text")}
name="data.content.0.children.0.children.0.children.0.text"
path={nodePath}
/>
<AttributeField.BackgroundColor
label={t("Background color")}
name="attributes.background-color"
path={nodePath}
/>
</Collapse.Item>
</CollapseWrapper>
</AttributesPanelWrapper>
);
};

// Override the default configuration panel
ConfigPanelsMap[ElementType.PAGE_HEADER] = CustomPageHeaderPanel;
ConfigPanelsMap[ElementType.PAGE_FOOTER] = CustomPageFooterPanel;

Comparison of Both Approaches

FeatureSchema ApproachReact Component Approach
ComplexitySimple, declarativeComplex, imperative
FlexibilitySuitable for standard config itemsSuitable for complex interaction logic
PerformanceBetterSlightly worse (requires component rendering)
RecommendedMost scenariosComplex logic or custom UI

Available Schema Field Types

The Schema approach supports multiple field types, commonly used ones include:

Basic Field Types:

  • TextField: Text input field
  • NumberField: Number input field
  • PixelField: Pixel value input (e.g., width, height)
  • ColorPickerField: Color picker
  • SelectField: Dropdown selection
  • SwitchField: Toggle switch
  • SliderField: Slider input
  • TextAreaField: Multi-line text input
  • ImageUploaderField: Image upload
  • RichTextField: Rich text editor

Composite Field Types:

  • BackgroundColor: Background color picker
  • ImageUrl: Image URL input
  • Padding: Padding settings (top, right, bottom, left)
  • Border: Border settings
  • BorderRadius: Border radius settings
  • Typography: Font style settings
  • TextAlign: Text alignment
  • Link: Link settings
  • BackgroundImage: Background image settings
  • BackgroundGradient: Background gradient settings

Container Types:

  • CollapseGroup: Collapsible group container
  • ResponsiveTabs: Responsive tabs (desktop/mobile)

For more field types, please refer to the type definitions in @easy-email-pro-theme/typings.

Priority Explanation

Configuration panel loading priority:

  1. Check BlockSchemasMap first: If a Schema definition exists, use Schema rendering
  2. Fallback to ConfigPanelsMap: If no Schema exists, use React component rendering
  3. If neither exists: No configuration panel is displayed

Therefore, if you override both BlockSchemasMap and ConfigPanelsMap, the system will prioritize using the Schema in BlockSchemasMap.

Complete Example

import { BlockSchemasMap, ConfigPanelsMap } from "easy-email-pro-theme";
import { ElementType } from "easy-email-pro-core";
import { Retro } from "easy-email-pro-theme";
import { EmailEditorProvider } from "easy-email-pro-editor";

// Method 1: Using Schema (Recommended)
import {
CustomPageHeaderSchema,
CustomPageFooterSchema,
} from "./custom-schemas";
BlockSchemasMap[ElementType.PAGE_HEADER] = CustomPageHeaderSchema;
BlockSchemasMap[ElementType.PAGE_FOOTER] = CustomPageFooterSchema;

// Or Method 2: Using React Component
// import { CustomPageHeaderPanel, CustomPageFooterPanel } from "./custom-panels";
// ConfigPanelsMap[ElementType.PAGE_HEADER] = CustomPageHeaderPanel;
// ConfigPanelsMap[ElementType.PAGE_FOOTER] = CustomPageFooterPanel;

export default function MyEditor() {
const config = Retro.useCreateConfig({
// ... other config
});

return (
<EmailEditorProvider {...config}>
<EditorHeader />
<Layout.Content>
<Retro.Layout />
</Layout.Content>
</EmailEditorProvider>
);
}

Features

  • Fixed at top/bottom: Blocks using PAGE_HEADER or PAGE_FOOTER category will automatically be fixed at the top or bottom of the email template
  • Cannot be dragged: These blocks cannot be moved by dragging
  • Cannot be deleted: Operation tools such as delete buttons will be hidden, and users cannot delete these blocks
  • Partially editable: Although they cannot be deleted or moved, partial content modification is allowed when editable: true:
    • Modify text content
    • Adjust background color
    • Modify style attributes
    • Edit child elements within the block
  • Editable property: The data.editable property determines whether editing interactions can be triggered. Only when editable: true can users edit the content within the blocks

Implementation Principle

The system determines whether a block is PAGE_HEADER or PAGE_FOOTER by checking the block's category property:

// Defined in blockCategory.ts
export const ElementCategory = {
PAGE_HEADER: "page-header" as const,
PAGE_FOOTER: "page-footer" as const,
// ... other categories
};

The editor will automatically recognize these categories and apply corresponding restrictions:

  • Dragging will be prevented
  • Delete tools will be hidden
  • Blocks will be fixed at their corresponding positions

Custom Extension

If the default PageHeader and PageFooter blocks cannot meet your needs, you can extend functionality by creating custom blocks (Custom Block).

Creating Custom Page Header

import { createBlock, ElementCategory, BasicType } from "easy-email-pro-core";
import { PageHeaderElement } from "easy-email-pro-core/typings";

export const CustomPageHeader = createBlock<PageHeaderElement>({
get name() {
return "Custom Header";
},
type: BasicType.PAGE_HEADER, // or use custom type
get category() {
return ElementCategory.PAGE_HEADER; // Key: Must use PAGE_HEADER category
},
defaultData: {
attributes: {
"background-color": "#ffffff",
"padding-top": "20px",
"padding-bottom": "20px",
},
data: {
content: [],
editable: true, // Set to true to enable editing interactions
},
},
// Custom rendering logic
render(params) {
// Your custom rendering implementation
return <YourCustomHeaderComponent {...params} />;
},
// Custom configuration panel
panel: [
// Your custom configuration items
],
});
import { createBlock, ElementCategory, BasicType } from "easy-email-pro-core";
import { PageFooterElement } from "easy-email-pro-core/typings";

export const CustomPageFooter = createBlock<PageFooterElement>({
get name() {
return "Custom Footer";
},
type: BasicType.PAGE_FOOTER, // or use custom type
get category() {
return ElementCategory.PAGE_FOOTER; // Key: Must use PAGE_FOOTER category
},
defaultData: {
attributes: {
"background-color": "#f5f5f5",
"padding-top": "20px",
"padding-bottom": "20px",
},
data: {
content: [],
editable: true, // Set to true to enable editing interactions
},
},
// Custom rendering logic
render(params) {
// Your custom rendering implementation
return <YourCustomFooterComponent {...params} />;
},
// Custom configuration panel
panel: [
// Your custom configuration items
],
});

Registering Custom Blocks

After creating custom blocks, you need to register them in the editor:

import { BlockManager } from "easy-email-pro-core";
import { CustomPageHeader, CustomPageFooter } from "./custom-blocks";

// Register custom blocks
BlockManager.registerBlocks([
CustomPageHeader,
CustomPageFooter,
// ... other custom blocks
]);

// Then use in editor configuration
export default function MyEditor() {
const config = Retro.useCreateConfig({
// ... other config
});

return (
<EmailEditorProvider {...config}>
<EditorHeader />
<Layout.Content>
<Retro.Layout />
</Layout.Content>
</EmailEditorProvider>
);
}

Notes for Custom Blocks

  1. Category must be correct: Custom blocks must use ElementCategory.PAGE_HEADER or ElementCategory.PAGE_FOOTER so that the editor can correctly identify and apply restrictions
  2. Type can be customized: Although the category must be fixed, the type can use custom values, allowing you to create multiple header or footer blocks with different styles
  3. Data structure: It is recommended to follow the data structure of PageHeaderElement or PageFooterElement to ensure compatibility with the system
  4. Editability control: The data.editable property controls whether editing interactions can be triggered:
    • editable: true: Enables editing interactions, allowing users to modify content within the block
    • editable: false: Disables editing interactions, preventing users from modifying content within the block

Use Cases

  1. Branding Requirements

    • Company logo in header
    • Legal disclaimers in footer
    • Copyright notices
  2. Subscription Features

    • Display watermarks for free users (Fully Frozen Approach)
    • Show premium features for paid users
    • Required subscription links that must be retained (Partially Editable Approach)
  3. Compliance Requirements

    • Required legal text
    • Unsubscribe links (Partially editable, allows style modification)
    • Contact information

Approach Comparison

FeatureFully Frozen Approach (headerElement/footerElement)Partially Editable Approach (PAGE_HEADER/PAGE_FOOTER)
Fixed Position
Draggable
Deletable
Editable Content✅ (partial)
Editable Style
Use CasesFully locked watermarks, logosRequired but adjustable links, text

Important Notes

  1. Fully Frozen Approach: Elements cannot be modified or deleted at all, suitable for scenarios requiring strict control
  2. Partially Editable Approach: Fixed at top/bottom, cannot be dragged or deleted, but allows partial content and style modification
  3. Elements from both approaches will be displayed in both edit and preview modes
  4. The same elements must be included when generating the final email HTML
  5. Both approaches support all standard block attributes and styles

For more examples and advanced usage, check our demo application.