6889 lines
256 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Build Power. Not Rent It. Own your digital infrastructure.">
<meta name="author" content="Bunker Operations">
<link rel="canonical" href="https://bnkserve.org/v2/features/pages/grapes-editor/">
<link rel="prev" href="../page-builder/">
<link rel="next" href="../block-library/">
<link rel="icon" href="../../../../assets/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.1">
<title>GrapesJS Editor - Changemaker Lite</title>
<link rel="stylesheet" href="../../../../assets/stylesheets/main.484c7ddc.min.css">
<link rel="stylesheet" href="../../../../assets/stylesheets/palette.ab4e12ef.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Inter";--md-code-font:"JetBrains Mono"}</style>
<link rel="stylesheet" href="../../../../stylesheets/extra.css">
<link rel="stylesheet" href="../../../../stylesheets/home.css">
<link rel="stylesheet" href="../../../../assets/css/video-player.css">
<script>__md_scope=new URL("../../../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
<meta property="og:type" content="website" />
<meta property="og:title" content="GrapesJS Editor - Changemaker Lite" />
<meta property="og:description" content="Build Power. Not Rent It. Own your digital infrastructure." />
<meta property="og:image" content="https://bnkserve.org/assets/images/social/v2/features/pages/grapes-editor.png" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:url" content="https://bnkserve.org/v2/features/pages/grapes-editor/" />
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:title" content="GrapesJS Editor - Changemaker Lite" />
<meta property="twitter:description" content="Build Power. Not Rent It. Own your digital infrastructure." />
<meta property="twitter:image" content="https://bnkserve.org/assets/images/social/v2/features/pages/grapes-editor.png" />
</head>
<body dir="ltr" data-md-color-scheme="slate" data-md-color-primary="deep-purple" data-md-color-accent="amber">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#grapesjs-editor-integration" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
<img src="../../../../assets/logo.png" alt="logo">
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
Changemaker Lite
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
GrapesJS Editor
</span>
</div>
</div>
</div>
<form class="md-header__option" data-md-component="palette">
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="deep-purple" data-md-color-accent="amber" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_0">
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m17.75 4.09-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94L12.44 4l1.06-3 1.06 3zm3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95zm-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31"/></svg>
</label>
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="deep-purple" data-md-color-accent="amber" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_0" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0-7 2.39 3.42C13.65 5.15 12.84 5 12 5s-1.65.15-2.39.42zM3.34 7l4.16-.35A7.2 7.2 0 0 0 5.94 8.5c-.44.74-.69 1.5-.83 2.29zm.02 10 1.76-3.77a7.131 7.131 0 0 0 2.38 4.14zM20.65 7l-1.77 3.79a7.02 7.02 0 0 0-2.38-4.15zm-.01 10-4.14.36c.59-.51 1.12-1.14 1.54-1.86.42-.73.69-1.5.83-2.29zM12 22l-2.41-3.44c.74.27 1.55.44 2.41.44.82 0 1.63-.17 2.37-.44z"/></svg>
</label>
</form>
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08"/></svg>
</a>
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</nav>
<div class="md-search__suggest" data-md-component="search-suggest"></div>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
<div class="md-header__source">
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
</div>
<div class="md-source__repository">
changemaker.lite
</div>
</a>
</div>
</nav>
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
<div class="md-grid">
<ul class="md-tabs__list">
<li class="md-tabs__item">
<a href="../../../.." class="md-tabs__link">
Home
</a>
</li>
<li class="md-tabs__item md-tabs__item--active">
<a href="../../../" class="md-tabs__link">
V2 Documentation
</a>
</li>
<li class="md-tabs__item">
<a href="../../../../phil/" class="md-tabs__link">
Philosophy
</a>
</li>
<li class="md-tabs__item">
<a href="../../../../v1/" class="md-tabs__link">
V1 Documentation (Legacy)
</a>
</li>
<li class="md-tabs__item">
<a href="../../../../blog/" class="md-tabs__link">
Blog
</a>
</li>
</ul>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../../../.." title="Changemaker Lite" class="md-nav__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
<img src="../../../../assets/logo.png" alt="logo">
</a>
Changemaker Lite
</label>
<div class="md-nav__source">
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
</div>
<div class="md-source__repository">
changemaker.lite
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../.." class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2" checked>
<div class="md-nav__link md-nav__container">
<a href="../../../" class="md-nav__link ">
<span class="md-ellipsis">
V2 Documentation
</span>
</a>
<label class="md-nav__link " for="__nav_2" id="__nav_2_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_2">
<span class="md-nav__icon md-icon"></span>
V2 Documentation
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_2" >
<div class="md-nav__link md-nav__container">
<a href="../../../getting-started/" class="md-nav__link ">
<span class="md-ellipsis">
Getting Started
</span>
</a>
<label class="md-nav__link " for="__nav_2_2" id="__nav_2_2_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_2">
<span class="md-nav__icon md-icon"></span>
Getting Started
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../getting-started/quick-start/" class="md-nav__link">
<span class="md-ellipsis">
Quick Start
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_3" >
<div class="md-nav__link md-nav__container">
<a href="../../../architecture/" class="md-nav__link ">
<span class="md-ellipsis">
Architecture
</span>
</a>
<label class="md-nav__link " for="__nav_2_3" id="__nav_2_3_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_3">
<span class="md-nav__icon md-icon"></span>
Architecture
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../architecture/dual-api/" class="md-nav__link">
<span class="md-ellipsis">
Dual API System
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../architecture/authentication/" class="md-nav__link">
<span class="md-ellipsis">
Authentication & Security
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_4" >
<div class="md-nav__link md-nav__container">
<a href="../../../backend/" class="md-nav__link ">
<span class="md-ellipsis">
Backend
</span>
</a>
<label class="md-nav__link " for="__nav_2_4" id="__nav_2_4_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_4">
<span class="md-nav__icon md-icon"></span>
Backend
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../backend/modules/" class="md-nav__link">
<span class="md-ellipsis">
Modules
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../backend/services/" class="md-nav__link">
<span class="md-ellipsis">
Services
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../backend/middleware/" class="md-nav__link">
<span class="md-ellipsis">
Middleware
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../backend/utilities/" class="md-nav__link">
<span class="md-ellipsis">
Utilities
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_5" >
<div class="md-nav__link md-nav__container">
<a href="../../../frontend/" class="md-nav__link ">
<span class="md-ellipsis">
Frontend
</span>
</a>
<label class="md-nav__link " for="__nav_2_5" id="__nav_2_5_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_5">
<span class="md-nav__icon md-icon"></span>
Frontend
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../frontend/components/" class="md-nav__link">
<span class="md-ellipsis">
Components
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../frontend/layouts/" class="md-nav__link">
<span class="md-ellipsis">
Layouts
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../frontend/pages/" class="md-nav__link">
<span class="md-ellipsis">
Pages
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_6" >
<div class="md-nav__link md-nav__container">
<a href="../../../database/" class="md-nav__link ">
<span class="md-ellipsis">
Database
</span>
</a>
<label class="md-nav__link " for="__nav_2_6" id="__nav_2_6_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_6_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_6">
<span class="md-nav__icon md-icon"></span>
Database
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../database/schema/" class="md-nav__link">
<span class="md-ellipsis">
Schema Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../database/migrations/" class="md-nav__link">
<span class="md-ellipsis">
Migrations
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../database/seeding/" class="md-nav__link">
<span class="md-ellipsis">
Seeding
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../database/indexes/" class="md-nav__link">
<span class="md-ellipsis">
Indexes
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../database/models/" class="md-nav__link">
<span class="md-ellipsis">
Models
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_7" checked>
<div class="md-nav__link md-nav__container">
<a href="../../" class="md-nav__link ">
<span class="md-ellipsis">
Features
</span>
</a>
<label class="md-nav__link " for="__nav_2_7" id="__nav_2_7_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_7_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_2_7">
<span class="md-nav__icon md-icon"></span>
Features
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../influence/" class="md-nav__link">
<span class="md-ellipsis">
Influence
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_7_4" checked>
<div class="md-nav__link md-nav__container">
<a href="../../landing-pages/" class="md-nav__link ">
<span class="md-ellipsis">
Landing Pages
</span>
</a>
<label class="md-nav__link " for="__nav_2_7_4" id="__nav_2_7_4_label" tabindex="0">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="3" aria-labelledby="__nav_2_7_4_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_2_7_4">
<span class="md-nav__icon md-icon"></span>
Landing Pages
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../page-builder/" class="md-nav__link">
<span class="md-ellipsis">
Page Builder
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
GrapesJS Editor
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
GrapesJS Editor
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="On this page">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
On this page
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#overview" class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
<nav class="md-nav" aria-label="Overview">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#key-features" class="md-nav__link">
<span class="md-ellipsis">
Key Features
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#architecture" class="md-nav__link">
<span class="md-ellipsis">
Architecture
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#component-api" class="md-nav__link">
<span class="md-ellipsis">
Component API
</span>
</a>
<nav class="md-nav" aria-label="Component API">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#props" class="md-nav__link">
<span class="md-ellipsis">
Props
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#ref-handle" class="md-nav__link">
<span class="md-ellipsis">
Ref Handle
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#usage-example" class="md-nav__link">
<span class="md-ellipsis">
Usage Example
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#grapesjs-configuration" class="md-nav__link">
<span class="md-ellipsis">
GrapesJS Configuration
</span>
</a>
<nav class="md-nav" aria-label="GrapesJS Configuration">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#initialization-options" class="md-nav__link">
<span class="md-ellipsis">
Initialization Options
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#plugins-ecosystem" class="md-nav__link">
<span class="md-ellipsis">
Plugins Ecosystem
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#custom-blocks-registration" class="md-nav__link">
<span class="md-ellipsis">
Custom Blocks Registration
</span>
</a>
<nav class="md-nav" aria-label="Custom Blocks Registration">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#block-registration-flow" class="md-nav__link">
<span class="md-ellipsis">
Block Registration Flow
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#block-generation-logic" class="md-nav__link">
<span class="md-ellipsis">
Block Generation Logic
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#built-in-block-templates" class="md-nav__link">
<span class="md-ellipsis">
Built-In Block Templates
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#save-command-integration" class="md-nav__link">
<span class="md-ellipsis">
Save Command Integration
</span>
</a>
<nav class="md-nav" aria-label="Save Command Integration">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#command-registration" class="md-nav__link">
<span class="md-ellipsis">
Command Registration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#keyboard-shortcut" class="md-nav__link">
<span class="md-ellipsis">
Keyboard Shortcut
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#forwardref-pattern" class="md-nav__link">
<span class="md-ellipsis">
forwardRef Pattern
</span>
</a>
<nav class="md-nav" aria-label="forwardRef Pattern">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#implementation" class="md-nav__link">
<span class="md-ellipsis">
Implementation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#parent-usage" class="md-nav__link">
<span class="md-ellipsis">
Parent Usage
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#error-handling" class="md-nav__link">
<span class="md-ellipsis">
Error Handling
</span>
</a>
<nav class="md-nav" aria-label="Error Handling">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#error-boundary-state" class="md-nav__link">
<span class="md-ellipsis">
Error Boundary State
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#parent-level-fallback" class="md-nav__link">
<span class="md-ellipsis">
Parent-Level Fallback
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#mobile-detection" class="md-nav__link">
<span class="md-ellipsis">
Mobile Detection
</span>
</a>
<nav class="md-nav" aria-label="Mobile Detection">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#desktop-only-warning" class="md-nav__link">
<span class="md-ellipsis">
Desktop-Only Warning
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#data-flow-patterns" class="md-nav__link">
<span class="md-ellipsis">
Data Flow Patterns
</span>
</a>
<nav class="md-nav" aria-label="Data Flow Patterns">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#initial-load" class="md-nav__link">
<span class="md-ellipsis">
Initial Load
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#save-flow" class="md-nav__link">
<span class="md-ellipsis">
Save Flow
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#code-examples" class="md-nav__link">
<span class="md-ellipsis">
Code Examples
</span>
</a>
<nav class="md-nav" aria-label="Code Examples">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#complete-integration-example" class="md-nav__link">
<span class="md-ellipsis">
Complete Integration Example
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#custom-block-registration" class="md-nav__link">
<span class="md-ellipsis">
Custom Block Registration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#adding-custom-html-generation" class="md-nav__link">
<span class="md-ellipsis">
Adding Custom HTML Generation
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#troubleshooting" class="md-nav__link">
<span class="md-ellipsis">
Troubleshooting
</span>
</a>
<nav class="md-nav" aria-label="Troubleshooting">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#problem-blocks-not-appearing-in-left-panel" class="md-nav__link">
<span class="md-ellipsis">
Problem: Blocks Not Appearing in Left Panel
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#problem-save-not-triggering" class="md-nav__link">
<span class="md-ellipsis">
Problem: Save Not Triggering
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#problem-editor-crashes-on-large-pages" class="md-nav__link">
<span class="md-ellipsis">
Problem: Editor Crashes on Large Pages
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#problem-initial-data-not-loading" class="md-nav__link">
<span class="md-ellipsis">
Problem: Initial Data Not Loading
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#problem-styles-not-applying-in-canvas" class="md-nav__link">
<span class="md-ellipsis">
Problem: Styles Not Applying in Canvas
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#performance-optimization" class="md-nav__link">
<span class="md-ellipsis">
Performance Optimization
</span>
</a>
<nav class="md-nav" aria-label="Performance Optimization">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#lazy-loading" class="md-nav__link">
<span class="md-ellipsis">
Lazy Loading
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#debounced-auto-save" class="md-nav__link">
<span class="md-ellipsis">
Debounced Auto-Save
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#related-documentation" class="md-nav__link">
<span class="md-ellipsis">
Related Documentation
</span>
</a>
<nav class="md-nav" aria-label="Related Documentation">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#components" class="md-nav__link">
<span class="md-ellipsis">
Components
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#features" class="md-nav__link">
<span class="md-ellipsis">
Features
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#external" class="md-nav__link">
<span class="md-ellipsis">
External
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../block-library/" class="md-nav__link">
<span class="md-ellipsis">
Block Library
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../mkdocs-export/" class="md-nav__link">
<span class="md-ellipsis">
MkDocs Export
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../email-templates/" class="md-nav__link">
<span class="md-ellipsis">
Email Templates
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../media/" class="md-nav__link">
<span class="md-ellipsis">
Media
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../newsletter/" class="md-nav__link">
<span class="md-ellipsis">
Newsletter
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../observability/" class="md-nav__link">
<span class="md-ellipsis">
Observability
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../tunnel/" class="md-nav__link">
<span class="md-ellipsis">
Tunnel
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_8" >
<div class="md-nav__link md-nav__container">
<a href="../../../deployment/" class="md-nav__link ">
<span class="md-ellipsis">
Deployment
</span>
</a>
<label class="md-nav__link " for="__nav_2_8" id="__nav_2_8_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_8_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_8">
<span class="md-nav__icon md-icon"></span>
Deployment
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../deployment/docker-compose/" class="md-nav__link">
<span class="md-ellipsis">
Docker Compose
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../deployment/environment-variables/" class="md-nav__link">
<span class="md-ellipsis">
Environment Variables
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../deployment/nginx/" class="md-nav__link">
<span class="md-ellipsis">
Nginx Configuration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../deployment/ssl-tls/" class="md-nav__link">
<span class="md-ellipsis">
SSL/TLS
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../deployment/tunneling/" class="md-nav__link">
<span class="md-ellipsis">
Tunneling
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../deployment/monitoring-stack/" class="md-nav__link">
<span class="md-ellipsis">
Monitoring Stack
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../deployment/healthchecks/" class="md-nav__link">
<span class="md-ellipsis">
Health Checks
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../deployment/scaling/" class="md-nav__link">
<span class="md-ellipsis">
Scaling
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../deployment/backup-restore/" class="md-nav__link">
<span class="md-ellipsis">
Backup & Restore
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_9" >
<div class="md-nav__link md-nav__container">
<a href="../../../development/" class="md-nav__link ">
<span class="md-ellipsis">
Development
</span>
</a>
<label class="md-nav__link " for="__nav_2_9" id="__nav_2_9_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_9_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_9">
<span class="md-nav__icon md-icon"></span>
Development
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../development/local-setup/" class="md-nav__link">
<span class="md-ellipsis">
Local Setup
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../development/docker-workflow/" class="md-nav__link">
<span class="md-ellipsis">
Docker Workflow
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../development/git-workflow/" class="md-nav__link">
<span class="md-ellipsis">
Git Workflow
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../development/npm-commands/" class="md-nav__link">
<span class="md-ellipsis">
NPM Commands
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../development/migrations/" class="md-nav__link">
<span class="md-ellipsis">
Migrations
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../development/typescript/" class="md-nav__link">
<span class="md-ellipsis">
TypeScript
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../development/testing/" class="md-nav__link">
<span class="md-ellipsis">
Testing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../development/debugging/" class="md-nav__link">
<span class="md-ellipsis">
Debugging
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../development/code-style/" class="md-nav__link">
<span class="md-ellipsis">
Code Style
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_10" >
<div class="md-nav__link md-nav__container">
<a href="../../../api-reference/" class="md-nav__link ">
<span class="md-ellipsis">
API Reference
</span>
</a>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_10_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_10">
<span class="md-nav__icon md-icon"></span>
API Reference
</label>
<ul class="md-nav__list" data-md-scrollfix>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_11" >
<div class="md-nav__link md-nav__container">
<a href="../../../user-guides/" class="md-nav__link ">
<span class="md-ellipsis">
User Guides
</span>
</a>
<label class="md-nav__link " for="__nav_2_11" id="__nav_2_11_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_11_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_11">
<span class="md-nav__icon md-icon"></span>
User Guides
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../user-guides/admin-guide/" class="md-nav__link">
<span class="md-ellipsis">
Admin Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../user-guides/campaign-manager-guide/" class="md-nav__link">
<span class="md-ellipsis">
Campaign Manager Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../user-guides/map-organizer-guide/" class="md-nav__link">
<span class="md-ellipsis">
Map Organizer Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../user-guides/content-editor-guide/" class="md-nav__link">
<span class="md-ellipsis">
Content Editor Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../user-guides/volunteer-guide/" class="md-nav__link">
<span class="md-ellipsis">
Volunteer Guide
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_12" >
<div class="md-nav__link md-nav__container">
<a href="../../../troubleshooting/" class="md-nav__link ">
<span class="md-ellipsis">
Troubleshooting
</span>
</a>
<label class="md-nav__link " for="__nav_2_12" id="__nav_2_12_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_12_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_12">
<span class="md-nav__icon md-icon"></span>
Troubleshooting
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../troubleshooting/faq/" class="md-nav__link">
<span class="md-ellipsis">
FAQ
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../troubleshooting/common-errors/" class="md-nav__link">
<span class="md-ellipsis">
Common Errors
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../troubleshooting/auth-issues/" class="md-nav__link">
<span class="md-ellipsis">
Auth Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../troubleshooting/database-issues/" class="md-nav__link">
<span class="md-ellipsis">
Database Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../troubleshooting/docker-issues/" class="md-nav__link">
<span class="md-ellipsis">
Docker Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../troubleshooting/email-issues/" class="md-nav__link">
<span class="md-ellipsis">
Email Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../troubleshooting/geocoding-issues/" class="md-nav__link">
<span class="md-ellipsis">
Geocoding Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../troubleshooting/monitoring-issues/" class="md-nav__link">
<span class="md-ellipsis">
Monitoring Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../troubleshooting/performance-optimization/" class="md-nav__link">
<span class="md-ellipsis">
Performance Optimization
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_13" >
<div class="md-nav__link md-nav__container">
<a href="../../../migration/" class="md-nav__link ">
<span class="md-ellipsis">
Migration
</span>
</a>
<label class="md-nav__link " for="__nav_2_13" id="__nav_2_13_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_13_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_13">
<span class="md-nav__icon md-icon"></span>
Migration
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../migration/feature-parity/" class="md-nav__link">
<span class="md-ellipsis">
Feature Parity
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../migration/breaking-changes/" class="md-nav__link">
<span class="md-ellipsis">
Breaking Changes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../migration/api-changes/" class="md-nav__link">
<span class="md-ellipsis">
API Changes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../migration/data-migration/" class="md-nav__link">
<span class="md-ellipsis">
Data Migration
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_14" >
<div class="md-nav__link md-nav__container">
<a href="../../../contributing/" class="md-nav__link ">
<span class="md-ellipsis">
Contributing
</span>
</a>
<label class="md-nav__link " for="__nav_2_14" id="__nav_2_14_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_14_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_14">
<span class="md-nav__icon md-icon"></span>
Contributing
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../../contributing/development-setup/" class="md-nav__link">
<span class="md-ellipsis">
Development Setup
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../contributing/code-of-conduct/" class="md-nav__link">
<span class="md-ellipsis">
Code of Conduct
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../contributing/pull-requests/" class="md-nav__link">
<span class="md-ellipsis">
Pull Requests
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../../contributing/roadmap/" class="md-nav__link">
<span class="md-ellipsis">
Roadmap
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../../phil/" class="md-nav__link">
<span class="md-ellipsis">
Philosophy
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../../v1/" class="md-nav__link">
<span class="md-ellipsis">
V1 Documentation (Legacy)
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../../blog/" class="md-nav__link">
<span class="md-ellipsis">
Blog
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="On this page">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
On this page
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#overview" class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
<nav class="md-nav" aria-label="Overview">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#key-features" class="md-nav__link">
<span class="md-ellipsis">
Key Features
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#architecture" class="md-nav__link">
<span class="md-ellipsis">
Architecture
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#component-api" class="md-nav__link">
<span class="md-ellipsis">
Component API
</span>
</a>
<nav class="md-nav" aria-label="Component API">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#props" class="md-nav__link">
<span class="md-ellipsis">
Props
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#ref-handle" class="md-nav__link">
<span class="md-ellipsis">
Ref Handle
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#usage-example" class="md-nav__link">
<span class="md-ellipsis">
Usage Example
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#grapesjs-configuration" class="md-nav__link">
<span class="md-ellipsis">
GrapesJS Configuration
</span>
</a>
<nav class="md-nav" aria-label="GrapesJS Configuration">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#initialization-options" class="md-nav__link">
<span class="md-ellipsis">
Initialization Options
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#plugins-ecosystem" class="md-nav__link">
<span class="md-ellipsis">
Plugins Ecosystem
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#custom-blocks-registration" class="md-nav__link">
<span class="md-ellipsis">
Custom Blocks Registration
</span>
</a>
<nav class="md-nav" aria-label="Custom Blocks Registration">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#block-registration-flow" class="md-nav__link">
<span class="md-ellipsis">
Block Registration Flow
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#block-generation-logic" class="md-nav__link">
<span class="md-ellipsis">
Block Generation Logic
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#built-in-block-templates" class="md-nav__link">
<span class="md-ellipsis">
Built-In Block Templates
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#save-command-integration" class="md-nav__link">
<span class="md-ellipsis">
Save Command Integration
</span>
</a>
<nav class="md-nav" aria-label="Save Command Integration">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#command-registration" class="md-nav__link">
<span class="md-ellipsis">
Command Registration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#keyboard-shortcut" class="md-nav__link">
<span class="md-ellipsis">
Keyboard Shortcut
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#forwardref-pattern" class="md-nav__link">
<span class="md-ellipsis">
forwardRef Pattern
</span>
</a>
<nav class="md-nav" aria-label="forwardRef Pattern">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#implementation" class="md-nav__link">
<span class="md-ellipsis">
Implementation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#parent-usage" class="md-nav__link">
<span class="md-ellipsis">
Parent Usage
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#error-handling" class="md-nav__link">
<span class="md-ellipsis">
Error Handling
</span>
</a>
<nav class="md-nav" aria-label="Error Handling">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#error-boundary-state" class="md-nav__link">
<span class="md-ellipsis">
Error Boundary State
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#parent-level-fallback" class="md-nav__link">
<span class="md-ellipsis">
Parent-Level Fallback
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#mobile-detection" class="md-nav__link">
<span class="md-ellipsis">
Mobile Detection
</span>
</a>
<nav class="md-nav" aria-label="Mobile Detection">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#desktop-only-warning" class="md-nav__link">
<span class="md-ellipsis">
Desktop-Only Warning
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#data-flow-patterns" class="md-nav__link">
<span class="md-ellipsis">
Data Flow Patterns
</span>
</a>
<nav class="md-nav" aria-label="Data Flow Patterns">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#initial-load" class="md-nav__link">
<span class="md-ellipsis">
Initial Load
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#save-flow" class="md-nav__link">
<span class="md-ellipsis">
Save Flow
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#code-examples" class="md-nav__link">
<span class="md-ellipsis">
Code Examples
</span>
</a>
<nav class="md-nav" aria-label="Code Examples">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#complete-integration-example" class="md-nav__link">
<span class="md-ellipsis">
Complete Integration Example
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#custom-block-registration" class="md-nav__link">
<span class="md-ellipsis">
Custom Block Registration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#adding-custom-html-generation" class="md-nav__link">
<span class="md-ellipsis">
Adding Custom HTML Generation
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#troubleshooting" class="md-nav__link">
<span class="md-ellipsis">
Troubleshooting
</span>
</a>
<nav class="md-nav" aria-label="Troubleshooting">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#problem-blocks-not-appearing-in-left-panel" class="md-nav__link">
<span class="md-ellipsis">
Problem: Blocks Not Appearing in Left Panel
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#problem-save-not-triggering" class="md-nav__link">
<span class="md-ellipsis">
Problem: Save Not Triggering
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#problem-editor-crashes-on-large-pages" class="md-nav__link">
<span class="md-ellipsis">
Problem: Editor Crashes on Large Pages
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#problem-initial-data-not-loading" class="md-nav__link">
<span class="md-ellipsis">
Problem: Initial Data Not Loading
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#problem-styles-not-applying-in-canvas" class="md-nav__link">
<span class="md-ellipsis">
Problem: Styles Not Applying in Canvas
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#performance-optimization" class="md-nav__link">
<span class="md-ellipsis">
Performance Optimization
</span>
</a>
<nav class="md-nav" aria-label="Performance Optimization">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#lazy-loading" class="md-nav__link">
<span class="md-ellipsis">
Lazy Loading
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#debounced-auto-save" class="md-nav__link">
<span class="md-ellipsis">
Debounced Auto-Save
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#related-documentation" class="md-nav__link">
<span class="md-ellipsis">
Related Documentation
</span>
</a>
<nav class="md-nav" aria-label="Related Documentation">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#components" class="md-nav__link">
<span class="md-ellipsis">
Components
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#features" class="md-nav__link">
<span class="md-ellipsis">
Features
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#external" class="md-nav__link">
<span class="md-ellipsis">
External
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<nav class="md-path" aria-label="Navigation" >
<ol class="md-path__list">
<li class="md-path__item">
<a href="../../../.." class="md-path__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-path__item">
<a href="../../../" class="md-path__link">
<span class="md-ellipsis">
V2 Documentation
</span>
</a>
</li>
<li class="md-path__item">
<a href="../../" class="md-path__link">
<span class="md-ellipsis">
Features
</span>
</a>
</li>
<li class="md-path__item">
<a href="../../landing-pages/" class="md-path__link">
<span class="md-ellipsis">
Landing Pages
</span>
</a>
</li>
</ol>
</nav>
<article class="md-content__inner md-typeset">
<a href="https://gitea.bnkops.com/admin/changemaker.lite/src/branch/main/mkdocs/docs/v2/features/pages/grapes-editor.md" title="Edit this page" class="md-content__button md-icon" rel="edit">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10 20H6V4h7v5h5v3.1l2-2V8l-6-6H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h4zm10.2-7c.1 0 .3.1.4.2l1.3 1.3c.2.2.2.6 0 .8l-1 1-2.1-2.1 1-1c.1-.1.2-.2.4-.2m0 3.9L14.1 23H12v-2.1l6.1-6.1z"/></svg>
</a>
<a href="https://gitea.bnkops.com/admin/changemaker.lite/src/branch/main/mkdocs/docs/v2/features/pages/grapes-rawor.md" title="View source of this page" class="md-content__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M17 18c.56 0 1 .44 1 1s-.44 1-1 1-1-.44-1-1 .44-1 1-1m0-3c-2.73 0-5.06 1.66-6 4 .94 2.34 3.27 4 6 4s5.06-1.66 6-4c-.94-2.34-3.27-4-6-4m0 6.5a2.5 2.5 0 0 1-2.5-2.5 2.5 2.5 0 0 1 2.5-2.5 2.5 2.5 0 0 1 2.5 2.5 2.5 2.5 0 0 1-2.5 2.5M9.27 20H6V4h7v5h5v4.07c.7.08 1.36.25 2 .49V8l-6-6H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h4.5a8.2 8.2 0 0 1-1.23-2"/></svg>
</a>
<h1 id="grapesjs-editor-integration">GrapesJS Editor Integration<a class="headerlink" href="#grapesjs-editor-integration" title="Permanent link">&para;</a></h1>
<p>React wrapper component for GrapesJS WYSIWYG editor with forwardRef pattern, custom block registration, and keyboard shortcuts.</p>
<hr />
<h2 id="overview">Overview<a class="headerlink" href="#overview" title="Permanent link">&para;</a></h2>
<p>The GrapesJS Editor component provides a production-ready integration of the GrapesJS page builder library into the Changemaker Lite admin interface. It handles initialization, plugin configuration, custom block registration, and save orchestration.</p>
<h3 id="key-features">Key Features<a class="headerlink" href="#key-features" title="Permanent link">&para;</a></h3>
<ul>
<li><strong>forwardRef Pattern</strong>: Parent components trigger save via ref handle</li>
<li><strong>Custom Block Library</strong>: Register campaign-specific blocks from database</li>
<li><strong>Plugin Ecosystem</strong>: 10+ GrapesJS plugins pre-configured</li>
<li><strong>Keyboard Shortcuts</strong>: Ctrl+S (Cmd+S on Mac) to save</li>
<li><strong>Error Boundary</strong>: Graceful fallback on initialization failure</li>
<li><strong>Mobile Detection</strong>: Desktop-only warning for small screens</li>
<li><strong>Video Block Support</strong>: Placeholder generation for media library videos</li>
</ul>
<hr />
<h2 id="architecture">Architecture<a class="headerlink" href="#architecture" title="Permanent link">&para;</a></h2>
<pre class="mermaid"><code>graph TD
A[LandingPageEditor] --&gt;|ref| B[GrapesJSEditor]
B --&gt;|useImperativeHandle| C[triggerSave handle]
B --&gt; D[grapesjs.init]
D --&gt; E[Load Plugins]
E --&gt; F[Register Custom Blocks]
F --&gt; G[Load Initial Data]
G --&gt; H[Canvas Ready]
A --&gt;|handleSave| I[editorRef.current.triggerSave]
I --&gt; J[Commands.run save-page]
J --&gt; K[getProjectData + getHtml + getCss]
K --&gt; L[onSave callback]
L --&gt; M[API PUT /pages/:id]
style B fill:#9d4edd
style D fill:#3498db
style M fill:#2ecc71</code></pre>
<p><strong>Flow:</strong></p>
<ol>
<li><strong>Mount</strong>: LandingPageEditor creates ref, renders GrapesJSEditor</li>
<li><strong>Init</strong>: GrapesJSEditor calls <code>grapesjs.init()</code> → Loads plugins</li>
<li><strong>Blocks</strong>: Registers custom blocks from PageBlock library</li>
<li><strong>Data</strong>: Loads <code>initialData</code> (GrapesJS projectData JSON)</li>
<li><strong>Expose</strong>: <code>useImperativeHandle</code> exposes <code>triggerSave()</code> method</li>
<li><strong>Save</strong>: Parent calls <code>editorRef.current.triggerSave()</code> → Runs <code>save-page</code> command</li>
<li><strong>Callback</strong>: GrapesJS extracts HTML/CSS → Calls <code>onSave()</code> → Parent saves to API</li>
</ol>
<hr />
<h2 id="component-api">Component API<a class="headerlink" href="#component-api" title="Permanent link">&para;</a></h2>
<h3 id="props">Props<a class="headerlink" href="#props" title="Permanent link">&para;</a></h3>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kd">interface</span><span class="w"> </span><span class="nx">GrapesJSEditorProps</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-0-2"><a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="w"> </span><span class="nx">initialData?</span><span class="o">:</span><span class="w"> </span><span class="kt">Record</span><span class="o">&lt;</span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">unknown</span><span class="o">&gt;</span><span class="p">;</span>
</span><span id="__span-0-3"><a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="w"> </span><span class="nx">onSave</span><span class="o">:</span><span class="w"> </span><span class="p">(</span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">projectData</span><span class="o">:</span><span class="w"> </span><span class="kt">Record</span><span class="o">&lt;</span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">unknown</span><span class="o">&gt;</span><span class="p">;</span><span class="w"> </span><span class="nx">html</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">;</span><span class="w"> </span><span class="nx">css</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="ow">void</span><span class="p">;</span>
</span><span id="__span-0-4"><a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="w"> </span><span class="nx">customBlocks?</span><span class="o">:</span><span class="w"> </span><span class="kt">PageBlock</span><span class="p">[];</span>
</span><span id="__span-0-5"><a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="p">}</span>
</span></code></pre></div>
<p><strong>Fields:</strong></p>
<ul>
<li><strong><code>initialData</code></strong> (optional): GrapesJS <code>projectData</code> JSON from previous save</li>
<li>Contains components tree, styles, assets</li>
<li>Empty object <code>{}</code> for new pages</li>
<li><strong><code>onSave</code></strong> (required): Callback when save triggered</li>
<li>Receives <code>{ projectData, html, css }</code></li>
<li>Parent responsibility: Send to API</li>
<li><strong><code>customBlocks</code></strong> (optional): Array of PageBlock records from database</li>
<li>Registered as draggable blocks in left panel</li>
<li>See <a href="../block-library/">Block Library</a> for schema</li>
</ul>
<h3 id="ref-handle">Ref Handle<a class="headerlink" href="#ref-handle" title="Permanent link">&para;</a></h3>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-1-1"><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="kd">interface</span><span class="w"> </span><span class="nx">GrapesJSEditorHandle</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-1-2"><a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a><span class="w"> </span><span class="nx">triggerSave</span><span class="o">:</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="ow">void</span><span class="p">;</span>
</span><span id="__span-1-3"><a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a><span class="p">}</span>
</span></code></pre></div>
<p><strong>Method:</strong></p>
<ul>
<li><strong><code>triggerSave()</code></strong>: Programmatically trigger save command</li>
<li>Extracts current editor state</li>
<li>Calls <code>onSave</code> callback</li>
<li>Used by parent's "Save" button or keyboard shortcut</li>
</ul>
<h3 id="usage-example">Usage Example<a class="headerlink" href="#usage-example" title="Permanent link">&para;</a></h3>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-2-1"><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">useRef</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span><span id="__span-2-2"><a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a><span class="k">import</span><span class="w"> </span><span class="nx">GrapesJSEditor</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">GrapesJSEditorHandle</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;@/components/GrapesJSEditor&#39;</span><span class="p">;</span>
</span><span id="__span-2-3"><a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a>
</span><span id="__span-2-4"><a id="__codelineno-2-4" name="__codelineno-2-4" href="#__codelineno-2-4"></a><span class="kd">function</span><span class="w"> </span><span class="nx">MyEditor</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-2-5"><a id="__codelineno-2-5" name="__codelineno-2-5" href="#__codelineno-2-5"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">editorRef</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">useRef</span><span class="o">&lt;</span><span class="nx">GrapesJSEditorHandle</span><span class="o">&gt;</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span><span id="__span-2-6"><a id="__codelineno-2-6" name="__codelineno-2-6" href="#__codelineno-2-6"></a>
</span><span id="__span-2-7"><a id="__codelineno-2-7" name="__codelineno-2-7" href="#__codelineno-2-7"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">handleSave</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="p">(</span><span class="nx">data</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-2-8"><a id="__codelineno-2-8" name="__codelineno-2-8" href="#__codelineno-2-8"></a><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">api</span><span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="s1">&#39;/pages/123&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-2-9"><a id="__codelineno-2-9" name="__codelineno-2-9" href="#__codelineno-2-9"></a><span class="w"> </span><span class="nx">blocks</span><span class="o">:</span><span class="w"> </span><span class="kt">data.projectData</span><span class="p">,</span>
</span><span id="__span-2-10"><a id="__codelineno-2-10" name="__codelineno-2-10" href="#__codelineno-2-10"></a><span class="w"> </span><span class="nx">htmlOutput</span><span class="o">:</span><span class="w"> </span><span class="kt">data.html</span><span class="p">,</span>
</span><span id="__span-2-11"><a id="__codelineno-2-11" name="__codelineno-2-11" href="#__codelineno-2-11"></a><span class="w"> </span><span class="nx">cssOutput</span><span class="o">:</span><span class="w"> </span><span class="kt">data.css</span><span class="p">,</span>
</span><span id="__span-2-12"><a id="__codelineno-2-12" name="__codelineno-2-12" href="#__codelineno-2-12"></a><span class="w"> </span><span class="p">});</span>
</span><span id="__span-2-13"><a id="__codelineno-2-13" name="__codelineno-2-13" href="#__codelineno-2-13"></a><span class="w"> </span><span class="p">};</span>
</span><span id="__span-2-14"><a id="__codelineno-2-14" name="__codelineno-2-14" href="#__codelineno-2-14"></a>
</span><span id="__span-2-15"><a id="__codelineno-2-15" name="__codelineno-2-15" href="#__codelineno-2-15"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">handleManualSave</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-2-16"><a id="__codelineno-2-16" name="__codelineno-2-16" href="#__codelineno-2-16"></a><span class="w"> </span><span class="nx">editorRef</span><span class="p">.</span><span class="nx">current</span><span class="o">?</span><span class="p">.</span><span class="nx">triggerSave</span><span class="p">();</span>
</span><span id="__span-2-17"><a id="__codelineno-2-17" name="__codelineno-2-17" href="#__codelineno-2-17"></a><span class="w"> </span><span class="p">};</span>
</span><span id="__span-2-18"><a id="__codelineno-2-18" name="__codelineno-2-18" href="#__codelineno-2-18"></a>
</span><span id="__span-2-19"><a id="__codelineno-2-19" name="__codelineno-2-19" href="#__codelineno-2-19"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="p">(</span>
</span><span id="__span-2-20"><a id="__codelineno-2-20" name="__codelineno-2-20" href="#__codelineno-2-20"></a><span class="w"> </span><span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span>
</span><span id="__span-2-21"><a id="__codelineno-2-21" name="__codelineno-2-21" href="#__codelineno-2-21"></a><span class="w"> </span><span class="o">&lt;</span><span class="nx">button</span><span class="w"> </span><span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="nx">handleManualSave</span><span class="p">}</span><span class="o">&gt;</span><span class="nx">Save</span><span class="o">&lt;</span><span class="err">/button&gt;</span>
</span><span id="__span-2-22"><a id="__codelineno-2-22" name="__codelineno-2-22" href="#__codelineno-2-22"></a><span class="w"> </span><span class="o">&lt;</span><span class="nx">GrapesJSEditor</span>
</span><span id="__span-2-23"><a id="__codelineno-2-23" name="__codelineno-2-23" href="#__codelineno-2-23"></a><span class="w"> </span><span class="nx">ref</span><span class="o">=</span><span class="p">{</span><span class="nx">editorRef</span><span class="p">}</span>
</span><span id="__span-2-24"><a id="__codelineno-2-24" name="__codelineno-2-24" href="#__codelineno-2-24"></a><span class="w"> </span><span class="nx">initialData</span><span class="o">=</span><span class="p">{</span><span class="nx">page</span><span class="p">.</span><span class="nx">blocks</span><span class="p">}</span>
</span><span id="__span-2-25"><a id="__codelineno-2-25" name="__codelineno-2-25" href="#__codelineno-2-25"></a><span class="w"> </span><span class="nx">onSave</span><span class="o">=</span><span class="p">{</span><span class="nx">handleSave</span><span class="p">}</span>
</span><span id="__span-2-26"><a id="__codelineno-2-26" name="__codelineno-2-26" href="#__codelineno-2-26"></a><span class="w"> </span><span class="nx">customBlocks</span><span class="o">=</span><span class="p">{</span><span class="nx">blocks</span><span class="p">}</span>
</span><span id="__span-2-27"><a id="__codelineno-2-27" name="__codelineno-2-27" href="#__codelineno-2-27"></a><span class="w"> </span><span class="o">/&gt;</span>
</span><span id="__span-2-28"><a id="__codelineno-2-28" name="__codelineno-2-28" href="#__codelineno-2-28"></a><span class="w"> </span><span class="o">&lt;</span><span class="err">/div&gt;</span>
</span><span id="__span-2-29"><a id="__codelineno-2-29" name="__codelineno-2-29" href="#__codelineno-2-29"></a><span class="w"> </span><span class="p">);</span>
</span><span id="__span-2-30"><a id="__codelineno-2-30" name="__codelineno-2-30" href="#__codelineno-2-30"></a><span class="p">}</span>
</span></code></pre></div>
<hr />
<h2 id="grapesjs-configuration">GrapesJS Configuration<a class="headerlink" href="#grapesjs-configuration" title="Permanent link">&para;</a></h2>
<h3 id="initialization-options">Initialization Options<a class="headerlink" href="#initialization-options" title="Permanent link">&para;</a></h3>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-3-1"><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">editor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">grapesjs</span><span class="p">.</span><span class="nx">init</span><span class="p">({</span>
</span><span id="__span-3-2"><a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a><span class="w"> </span><span class="nx">container</span><span class="o">:</span><span class="w"> </span><span class="kt">containerRef.current</span><span class="p">,</span>
</span><span id="__span-3-3"><a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a><span class="w"> </span><span class="nx">height</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;100%&#39;</span><span class="p">,</span>
</span><span id="__span-3-4"><a id="__codelineno-3-4" name="__codelineno-3-4" href="#__codelineno-3-4"></a><span class="w"> </span><span class="nx">width</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;auto&#39;</span><span class="p">,</span>
</span><span id="__span-3-5"><a id="__codelineno-3-5" name="__codelineno-3-5" href="#__codelineno-3-5"></a><span class="w"> </span><span class="nx">storageManager</span><span class="o">:</span><span class="w"> </span><span class="kt">false</span><span class="p">,</span><span class="w"> </span><span class="c1">// No localStorage persistence (managed by API)</span>
</span><span id="__span-3-6"><a id="__codelineno-3-6" name="__codelineno-3-6" href="#__codelineno-3-6"></a><span class="w"> </span><span class="nx">plugins</span><span class="o">:</span><span class="w"> </span><span class="p">[</span>
</span><span id="__span-3-7"><a id="__codelineno-3-7" name="__codelineno-3-7" href="#__codelineno-3-7"></a><span class="w"> </span><span class="nx">blocksBasicPlugin</span><span class="p">,</span>
</span><span id="__span-3-8"><a id="__codelineno-3-8" name="__codelineno-3-8" href="#__codelineno-3-8"></a><span class="w"> </span><span class="nx">presetWebpagePlugin</span><span class="p">,</span>
</span><span id="__span-3-9"><a id="__codelineno-3-9" name="__codelineno-3-9" href="#__codelineno-3-9"></a><span class="w"> </span><span class="nx">formsPlugin</span><span class="p">,</span>
</span><span id="__span-3-10"><a id="__codelineno-3-10" name="__codelineno-3-10" href="#__codelineno-3-10"></a><span class="w"> </span><span class="nx">navbarPlugin</span><span class="p">,</span>
</span><span id="__span-3-11"><a id="__codelineno-3-11" name="__codelineno-3-11" href="#__codelineno-3-11"></a><span class="w"> </span><span class="nx">countdownPlugin</span><span class="p">,</span>
</span><span id="__span-3-12"><a id="__codelineno-3-12" name="__codelineno-3-12" href="#__codelineno-3-12"></a><span class="w"> </span><span class="nx">tabsPlugin</span><span class="p">,</span>
</span><span id="__span-3-13"><a id="__codelineno-3-13" name="__codelineno-3-13" href="#__codelineno-3-13"></a><span class="w"> </span><span class="nx">typedPlugin</span><span class="p">,</span>
</span><span id="__span-3-14"><a id="__codelineno-3-14" name="__codelineno-3-14" href="#__codelineno-3-14"></a><span class="w"> </span><span class="nx">customCodePlugin</span><span class="p">,</span>
</span><span id="__span-3-15"><a id="__codelineno-3-15" name="__codelineno-3-15" href="#__codelineno-3-15"></a><span class="w"> </span><span class="nx">exportPlugin</span><span class="p">,</span>
</span><span id="__span-3-16"><a id="__codelineno-3-16" name="__codelineno-3-16" href="#__codelineno-3-16"></a><span class="w"> </span><span class="nx">styleGradientPlugin</span><span class="p">,</span>
</span><span id="__span-3-17"><a id="__codelineno-3-17" name="__codelineno-3-17" href="#__codelineno-3-17"></a><span class="w"> </span><span class="nx">touchPlugin</span><span class="p">,</span>
</span><span id="__span-3-18"><a id="__codelineno-3-18" name="__codelineno-3-18" href="#__codelineno-3-18"></a><span class="w"> </span><span class="p">],</span>
</span><span id="__span-3-19"><a id="__codelineno-3-19" name="__codelineno-3-19" href="#__codelineno-3-19"></a><span class="w"> </span><span class="nx">pluginsOpts</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-3-20"><a id="__codelineno-3-20" name="__codelineno-3-20" href="#__codelineno-3-20"></a><span class="w"> </span><span class="p">[</span><span class="nx">blocksBasicPlugin</span><span class="p">]</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">flexGrid</span><span class="o">:</span><span class="w"> </span><span class="kt">true</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-3-21"><a id="__codelineno-3-21" name="__codelineno-3-21" href="#__codelineno-3-21"></a><span class="w"> </span><span class="c1">// ... other plugin options</span>
</span><span id="__span-3-22"><a id="__codelineno-3-22" name="__codelineno-3-22" href="#__codelineno-3-22"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-3-23"><a id="__codelineno-3-23" name="__codelineno-3-23" href="#__codelineno-3-23"></a><span class="w"> </span><span class="nx">canvas</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-3-24"><a id="__codelineno-3-24" name="__codelineno-3-24" href="#__codelineno-3-24"></a><span class="w"> </span><span class="nx">styles</span><span class="o">:</span><span class="w"> </span><span class="p">[</span>
</span><span id="__span-3-25"><a id="__codelineno-3-25" name="__codelineno-3-25" href="#__codelineno-3-25"></a><span class="w"> </span><span class="s1">&#39;https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;display=swap&#39;</span><span class="p">,</span>
</span><span id="__span-3-26"><a id="__codelineno-3-26" name="__codelineno-3-26" href="#__codelineno-3-26"></a><span class="w"> </span><span class="p">],</span>
</span><span id="__span-3-27"><a id="__codelineno-3-27" name="__codelineno-3-27" href="#__codelineno-3-27"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-3-28"><a id="__codelineno-3-28" name="__codelineno-3-28" href="#__codelineno-3-28"></a><span class="p">});</span>
</span></code></pre></div>
<p><strong>Key Settings:</strong></p>
<ul>
<li><strong><code>storageManager: false</code></strong>: Disables auto-save to localStorage (we use API persistence)</li>
<li><strong><code>height: '100%'</code></strong>: Fills parent container (full-screen editor)</li>
<li><strong><code>canvas.styles</code></strong>: Injects Google Fonts into preview iframe</li>
</ul>
<h3 id="plugins-ecosystem">Plugins Ecosystem<a class="headerlink" href="#plugins-ecosystem" title="Permanent link">&para;</a></h3>
<table>
<thead>
<tr>
<th>Plugin</th>
<th>Purpose</th>
<th>Features</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>grapesjs-blocks-basic</code></td>
<td>Basic blocks</td>
<td>Section, text, image, video, map, link, flexGrid</td>
</tr>
<tr>
<td><code>grapesjs-preset-webpage</code></td>
<td>Full page presets</td>
<td>Header, footer, hero templates</td>
</tr>
<tr>
<td><code>grapesjs-plugin-forms</code></td>
<td>Form components</td>
<td>Input, textarea, select, button, checkbox, radio</td>
</tr>
<tr>
<td><code>grapesjs-navbar</code></td>
<td>Navigation bars</td>
<td>Responsive navbar with dropdowns</td>
</tr>
<tr>
<td><code>grapesjs-component-countdown</code></td>
<td>Countdown timers</td>
<td>Event countdown with custom styling</td>
</tr>
<tr>
<td><code>grapesjs-tabs</code></td>
<td>Tab panels</td>
<td>Horizontal/vertical tab containers</td>
</tr>
<tr>
<td><code>grapesjs-typed</code></td>
<td>Typing animation</td>
<td>Typewriter text effect</td>
</tr>
<tr>
<td><code>grapesjs-custom-code</code></td>
<td>Embed raw HTML/JS</td>
<td>Custom code blocks (advanced users)</td>
</tr>
<tr>
<td><code>grapesjs-plugin-export</code></td>
<td>Export templates</td>
<td>ZIP download of HTML/CSS/assets</td>
</tr>
<tr>
<td><code>grapesjs-style-gradient</code></td>
<td>Gradient editor</td>
<td>Visual gradient picker for backgrounds</td>
</tr>
<tr>
<td><code>grapesjs-touch</code></td>
<td>Touch support</td>
<td>Mobile/tablet drag-and-drop (experimental)</td>
</tr>
</tbody>
</table>
<p><strong>Installation:</strong></p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-4-1"><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="nb">cd</span><span class="w"> </span>admin<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span>npm<span class="w"> </span>install<span class="w"> </span><span class="se">\</span>
</span><span id="__span-4-2"><a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a><span class="w"> </span>grapesjs<span class="w"> </span><span class="se">\</span>
</span><span id="__span-4-3"><a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a><span class="w"> </span>grapesjs-blocks-basic<span class="w"> </span><span class="se">\</span>
</span><span id="__span-4-4"><a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a><span class="w"> </span>grapesjs-preset-webpage<span class="w"> </span><span class="se">\</span>
</span><span id="__span-4-5"><a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a><span class="w"> </span>grapesjs-plugin-forms<span class="w"> </span><span class="se">\</span>
</span><span id="__span-4-6"><a id="__codelineno-4-6" name="__codelineno-4-6" href="#__codelineno-4-6"></a><span class="w"> </span>grapesjs-navbar<span class="w"> </span><span class="se">\</span>
</span><span id="__span-4-7"><a id="__codelineno-4-7" name="__codelineno-4-7" href="#__codelineno-4-7"></a><span class="w"> </span>grapesjs-component-countdown<span class="w"> </span><span class="se">\</span>
</span><span id="__span-4-8"><a id="__codelineno-4-8" name="__codelineno-4-8" href="#__codelineno-4-8"></a><span class="w"> </span>grapesjs-tabs<span class="w"> </span><span class="se">\</span>
</span><span id="__span-4-9"><a id="__codelineno-4-9" name="__codelineno-4-9" href="#__codelineno-4-9"></a><span class="w"> </span>grapesjs-typed<span class="w"> </span><span class="se">\</span>
</span><span id="__span-4-10"><a id="__codelineno-4-10" name="__codelineno-4-10" href="#__codelineno-4-10"></a><span class="w"> </span>grapesjs-custom-code<span class="w"> </span><span class="se">\</span>
</span><span id="__span-4-11"><a id="__codelineno-4-11" name="__codelineno-4-11" href="#__codelineno-4-11"></a><span class="w"> </span>grapesjs-plugin-export<span class="w"> </span><span class="se">\</span>
</span><span id="__span-4-12"><a id="__codelineno-4-12" name="__codelineno-4-12" href="#__codelineno-4-12"></a><span class="w"> </span>grapesjs-style-gradient<span class="w"> </span><span class="se">\</span>
</span><span id="__span-4-13"><a id="__codelineno-4-13" name="__codelineno-4-13" href="#__codelineno-4-13"></a><span class="w"> </span>grapesjs-touch
</span></code></pre></div>
<hr />
<h2 id="custom-blocks-registration">Custom Blocks Registration<a class="headerlink" href="#custom-blocks-registration" title="Permanent link">&para;</a></h2>
<h3 id="block-registration-flow">Block Registration Flow<a class="headerlink" href="#block-registration-flow" title="Permanent link">&para;</a></h3>
<pre class="mermaid"><code>sequenceDiagram
participant API as API Database
participant Parent as LandingPageEditor
participant Editor as GrapesJSEditor
participant GJS as GrapesJS
Parent-&gt;&gt;API: GET /api/page-blocks
API--&gt;&gt;Parent: PageBlock[]
Parent-&gt;&gt;Editor: &lt;GrapesJSEditor customBlocks={blocks} /&gt;
Editor-&gt;&gt;Editor: useEffect(() =&gt; init)
Editor-&gt;&gt;GJS: grapesjs.init()
GJS--&gt;&gt;Editor: editor instance
Editor-&gt;&gt;Editor: Register custom blocks loop
loop For each block
Editor-&gt;&gt;Editor: generateBlockHtml(type, defaults)
Editor-&gt;&gt;GJS: BlockManager.add(id, config)
end
GJS--&gt;&gt;Editor: Blocks ready</code></pre>
<h3 id="block-generation-logic">Block Generation Logic<a class="headerlink" href="#block-generation-logic" title="Permanent link">&para;</a></h3>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-5-1"><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="c1">// From GrapesJSEditor.tsx</span>
</span><span id="__span-5-2"><a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a><span class="kd">const</span><span class="w"> </span><span class="nx">blockManager</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">editor</span><span class="p">.</span><span class="nx">Blocks</span><span class="p">;</span>
</span><span id="__span-5-3"><a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kd">const</span><span class="w"> </span><span class="nx">block</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nx">customBlocks</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-5-4"><a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">defaults</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">block</span><span class="p">.</span><span class="nx">defaults</span><span class="w"> </span><span class="kr">as</span><span class="w"> </span><span class="nx">Record</span><span class="o">&lt;</span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">unknown</span><span class="o">&gt;</span><span class="p">;</span>
</span><span id="__span-5-5"><a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">html</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">generateBlockHtml</span><span class="p">(</span><span class="nx">block</span><span class="p">.</span><span class="kr">type</span><span class="p">,</span><span class="w"> </span><span class="nx">defaults</span><span class="p">);</span>
</span><span id="__span-5-6"><a id="__codelineno-5-6" name="__codelineno-5-6" href="#__codelineno-5-6"></a>
</span><span id="__span-5-7"><a id="__codelineno-5-7" name="__codelineno-5-7" href="#__codelineno-5-7"></a><span class="w"> </span><span class="nx">blockManager</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="sb">`custom-</span><span class="si">${</span><span class="nx">block</span><span class="p">.</span><span class="kr">type</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-5-8"><a id="__codelineno-5-8" name="__codelineno-5-8" href="#__codelineno-5-8"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="kt">block.label</span><span class="p">,</span>
</span><span id="__span-5-9"><a id="__codelineno-5-9" name="__codelineno-5-9" href="#__codelineno-5-9"></a><span class="w"> </span><span class="nx">category</span><span class="o">:</span><span class="w"> </span><span class="kt">block.category</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;Campaign&#39;</span><span class="p">,</span>
</span><span id="__span-5-10"><a id="__codelineno-5-10" name="__codelineno-5-10" href="#__codelineno-5-10"></a><span class="w"> </span><span class="nx">content</span><span class="o">:</span><span class="w"> </span><span class="kt">html</span><span class="p">,</span>
</span><span id="__span-5-11"><a id="__codelineno-5-11" name="__codelineno-5-11" href="#__codelineno-5-11"></a><span class="w"> </span><span class="p">});</span>
</span><span id="__span-5-12"><a id="__codelineno-5-12" name="__codelineno-5-12" href="#__codelineno-5-12"></a><span class="p">}</span>
</span></code></pre></div>
<p><strong>Example Block:</strong></p>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-6-1"><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="c1">// From seed.ts</span>
</span><span id="__span-6-2"><a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a><span class="p">{</span>
</span><span id="__span-6-3"><a id="__codelineno-6-3" name="__codelineno-6-3" href="#__codelineno-6-3"></a><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;default-hero&#39;</span><span class="p">,</span>
</span><span id="__span-6-4"><a id="__codelineno-6-4" name="__codelineno-6-4" href="#__codelineno-6-4"></a><span class="w"> </span><span class="kr">type</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;hero&#39;</span><span class="p">,</span>
</span><span id="__span-6-5"><a id="__codelineno-6-5" name="__codelineno-6-5" href="#__codelineno-6-5"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Hero Section&#39;</span><span class="p">,</span>
</span><span id="__span-6-6"><a id="__codelineno-6-6" name="__codelineno-6-6" href="#__codelineno-6-6"></a><span class="w"> </span><span class="nx">category</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Headers&#39;</span><span class="p">,</span>
</span><span id="__span-6-7"><a id="__codelineno-6-7" name="__codelineno-6-7" href="#__codelineno-6-7"></a><span class="w"> </span><span class="nx">defaults</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-6-8"><a id="__codelineno-6-8" name="__codelineno-6-8" href="#__codelineno-6-8"></a><span class="w"> </span><span class="nx">title</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Welcome to Our Campaign&#39;</span><span class="p">,</span>
</span><span id="__span-6-9"><a id="__codelineno-6-9" name="__codelineno-6-9" href="#__codelineno-6-9"></a><span class="w"> </span><span class="nx">subtitle</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Join us in making a difference.&#39;</span><span class="p">,</span>
</span><span id="__span-6-10"><a id="__codelineno-6-10" name="__codelineno-6-10" href="#__codelineno-6-10"></a><span class="w"> </span><span class="nx">ctaText</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Get Involved&#39;</span><span class="p">,</span>
</span><span id="__span-6-11"><a id="__codelineno-6-11" name="__codelineno-6-11" href="#__codelineno-6-11"></a><span class="w"> </span><span class="nx">ctaUrl</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;#&#39;</span><span class="p">,</span>
</span><span id="__span-6-12"><a id="__codelineno-6-12" name="__codelineno-6-12" href="#__codelineno-6-12"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-6-13"><a id="__codelineno-6-13" name="__codelineno-6-13" href="#__codelineno-6-13"></a><span class="p">}</span>
</span></code></pre></div>
<p><strong>Generated HTML:</strong></p>
<div class="language-html highlight"><pre><span></span><code><span id="__span-7-1"><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a><span class="p">&lt;</span><span class="nt">section</span> <span class="na">style</span><span class="o">=</span><span class="s">&quot;padding: 80px 40px; text-align: center; background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); color: #fff;&quot;</span><span class="p">&gt;</span>
</span><span id="__span-7-2"><a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a> <span class="p">&lt;</span><span class="nt">h1</span> <span class="na">style</span><span class="o">=</span><span class="s">&quot;font-size: 2.5rem; margin-bottom: 16px;&quot;</span><span class="p">&gt;</span>Welcome to Our Campaign<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span><span id="__span-7-3"><a id="__codelineno-7-3" name="__codelineno-7-3" href="#__codelineno-7-3"></a> <span class="p">&lt;</span><span class="nt">p</span> <span class="na">style</span><span class="o">=</span><span class="s">&quot;font-size: 1.25rem; opacity: 0.85; margin-bottom: 32px;&quot;</span><span class="p">&gt;</span>Join us in making a difference.<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span><span id="__span-7-4"><a id="__codelineno-7-4" name="__codelineno-7-4" href="#__codelineno-7-4"></a> <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&quot;#&quot;</span> <span class="na">style</span><span class="o">=</span><span class="s">&quot;display: inline-block; padding: 12px 32px; background: #9d4edd; color: #fff; text-decoration: none; border-radius: 6px; font-weight: 600;&quot;</span><span class="p">&gt;</span>Get Involved<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span><span id="__span-7-5"><a id="__codelineno-7-5" name="__codelineno-7-5" href="#__codelineno-7-5"></a><span class="p">&lt;/</span><span class="nt">section</span><span class="p">&gt;</span>
</span></code></pre></div>
<h3 id="built-in-block-templates">Built-In Block Templates<a class="headerlink" href="#built-in-block-templates" title="Permanent link">&para;</a></h3>
<p><strong>1. Hero Section</strong></p>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-8-1"><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="k">case</span><span class="w"> </span><span class="s1">&#39;hero&#39;</span><span class="o">:</span>
</span><span id="__span-8-2"><a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="sb">`</span>
</span><span id="__span-8-3"><a id="__codelineno-8-3" name="__codelineno-8-3" href="#__codelineno-8-3"></a><span class="sb"> &lt;section style=&quot;padding: 80px 40px; text-align: center; background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); color: #fff;&quot;&gt;</span>
</span><span id="__span-8-4"><a id="__codelineno-8-4" name="__codelineno-8-4" href="#__codelineno-8-4"></a><span class="sb"> &lt;h1 style=&quot;font-size: 2.5rem; margin-bottom: 16px;&quot;&gt;</span><span class="si">${</span><span class="nx">defaults</span><span class="p">.</span><span class="nx">title</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;Hero Title&#39;</span><span class="si">}</span><span class="sb">&lt;/h1&gt;</span>
</span><span id="__span-8-5"><a id="__codelineno-8-5" name="__codelineno-8-5" href="#__codelineno-8-5"></a><span class="sb"> &lt;p style=&quot;font-size: 1.25rem; opacity: 0.85; margin-bottom: 32px;&quot;&gt;</span><span class="si">${</span><span class="nx">defaults</span><span class="p">.</span><span class="nx">subtitle</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;Subtitle text here&#39;</span><span class="si">}</span><span class="sb">&lt;/p&gt;</span>
</span><span id="__span-8-6"><a id="__codelineno-8-6" name="__codelineno-8-6" href="#__codelineno-8-6"></a><span class="sb"> &lt;a href=&quot;</span><span class="si">${</span><span class="nx">defaults</span><span class="p">.</span><span class="nx">ctaUrl</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;#&#39;</span><span class="si">}</span><span class="sb">&quot; style=&quot;display: inline-block; padding: 12px 32px; background: #9d4edd; color: #fff; text-decoration: none; border-radius: 6px; font-weight: 600;&quot;&gt;</span><span class="si">${</span><span class="nx">defaults</span><span class="p">.</span><span class="nx">ctaText</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;Get Started&#39;</span><span class="si">}</span><span class="sb">&lt;/a&gt;</span>
</span><span id="__span-8-7"><a id="__codelineno-8-7" name="__codelineno-8-7" href="#__codelineno-8-7"></a><span class="sb"> &lt;/section&gt;`</span><span class="p">;</span>
</span></code></pre></div>
<p><strong>2. Text Block</strong></p>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-9-1"><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a><span class="k">case</span><span class="w"> </span><span class="s1">&#39;text&#39;</span><span class="o">:</span>
</span><span id="__span-9-2"><a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="sb">`</span>
</span><span id="__span-9-3"><a id="__codelineno-9-3" name="__codelineno-9-3" href="#__codelineno-9-3"></a><span class="sb"> &lt;section style=&quot;padding: 60px 40px; max-width: 800px; margin: 0 auto;&quot;&gt;</span>
</span><span id="__span-9-4"><a id="__codelineno-9-4" name="__codelineno-9-4" href="#__codelineno-9-4"></a><span class="sb"> &lt;h2 style=&quot;font-size: 1.75rem; margin-bottom: 16px;&quot;&gt;</span><span class="si">${</span><span class="nx">defaults</span><span class="p">.</span><span class="nx">heading</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;Heading&#39;</span><span class="si">}</span><span class="sb">&lt;/h2&gt;</span>
</span><span id="__span-9-5"><a id="__codelineno-9-5" name="__codelineno-9-5" href="#__codelineno-9-5"></a><span class="sb"> &lt;p style=&quot;font-size: 1rem; line-height: 1.7; opacity: 0.85;&quot;&gt;</span><span class="si">${</span><span class="nx">defaults</span><span class="p">.</span><span class="nx">body</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;Body text goes here.&#39;</span><span class="si">}</span><span class="sb">&lt;/p&gt;</span>
</span><span id="__span-9-6"><a id="__codelineno-9-6" name="__codelineno-9-6" href="#__codelineno-9-6"></a><span class="sb"> &lt;/section&gt;`</span><span class="p">;</span>
</span></code></pre></div>
<p><strong>3. Features Grid</strong></p>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-10-1"><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a><span class="k">case</span><span class="w"> </span><span class="s1">&#39;features&#39;</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-10-2"><a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">features</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">defaults</span><span class="p">.</span><span class="nx">features</span><span class="w"> </span><span class="kr">as</span><span class="w"> </span><span class="nb">Array</span><span class="o">&lt;</span><span class="p">{</span><span class="w"> </span><span class="nx">title</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">;</span><span class="w"> </span><span class="nx">description</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">}</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">[];</span>
</span><span id="__span-10-3"><a id="__codelineno-10-3" name="__codelineno-10-3" href="#__codelineno-10-3"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">featureHtml</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">features</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">f</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="sb">`</span>
</span><span id="__span-10-4"><a id="__codelineno-10-4" name="__codelineno-10-4" href="#__codelineno-10-4"></a><span class="sb"> &lt;div style=&quot;flex: 1; min-width: 250px; padding: 24px; text-align: center;&quot;&gt;</span>
</span><span id="__span-10-5"><a id="__codelineno-10-5" name="__codelineno-10-5" href="#__codelineno-10-5"></a><span class="sb"> &lt;h3 style=&quot;font-size: 1.25rem; margin-bottom: 8px;&quot;&gt;</span><span class="si">${</span><span class="nx">f</span><span class="p">.</span><span class="nx">title</span><span class="si">}</span><span class="sb">&lt;/h3&gt;</span>
</span><span id="__span-10-6"><a id="__codelineno-10-6" name="__codelineno-10-6" href="#__codelineno-10-6"></a><span class="sb"> &lt;p style=&quot;opacity: 0.8;&quot;&gt;</span><span class="si">${</span><span class="nx">f</span><span class="p">.</span><span class="nx">description</span><span class="si">}</span><span class="sb">&lt;/p&gt;</span>
</span><span id="__span-10-7"><a id="__codelineno-10-7" name="__codelineno-10-7" href="#__codelineno-10-7"></a><span class="sb"> &lt;/div&gt;`</span><span class="p">).</span><span class="nx">join</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">);</span>
</span><span id="__span-10-8"><a id="__codelineno-10-8" name="__codelineno-10-8" href="#__codelineno-10-8"></a>
</span><span id="__span-10-9"><a id="__codelineno-10-9" name="__codelineno-10-9" href="#__codelineno-10-9"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="sb">`</span>
</span><span id="__span-10-10"><a id="__codelineno-10-10" name="__codelineno-10-10" href="#__codelineno-10-10"></a><span class="sb"> &lt;section style=&quot;padding: 60px 40px;&quot;&gt;</span>
</span><span id="__span-10-11"><a id="__codelineno-10-11" name="__codelineno-10-11" href="#__codelineno-10-11"></a><span class="sb"> &lt;div style=&quot;display: flex; flex-wrap: wrap; gap: 24px; justify-content: center;&quot;&gt;</span>
</span><span id="__span-10-12"><a id="__codelineno-10-12" name="__codelineno-10-12" href="#__codelineno-10-12"></a><span class="sb"> </span><span class="si">${</span><span class="nx">featureHtml</span><span class="si">}</span>
</span><span id="__span-10-13"><a id="__codelineno-10-13" name="__codelineno-10-13" href="#__codelineno-10-13"></a><span class="sb"> &lt;/div&gt;</span>
</span><span id="__span-10-14"><a id="__codelineno-10-14" name="__codelineno-10-14" href="#__codelineno-10-14"></a><span class="sb"> &lt;/section&gt;`</span><span class="p">;</span>
</span><span id="__span-10-15"><a id="__codelineno-10-15" name="__codelineno-10-15" href="#__codelineno-10-15"></a><span class="p">}</span>
</span></code></pre></div>
<p><strong>4. Call to Action</strong></p>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-11-1"><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a><span class="k">case</span><span class="w"> </span><span class="s1">&#39;cta&#39;</span><span class="o">:</span>
</span><span id="__span-11-2"><a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="sb">`</span>
</span><span id="__span-11-3"><a id="__codelineno-11-3" name="__codelineno-11-3" href="#__codelineno-11-3"></a><span class="sb"> &lt;section style=&quot;padding: 60px 40px; text-align: center; background: linear-gradient(135deg, #9d4edd 0%, #7b2cbf 100%); color: #fff;&quot;&gt;</span>
</span><span id="__span-11-4"><a id="__codelineno-11-4" name="__codelineno-11-4" href="#__codelineno-11-4"></a><span class="sb"> &lt;h2 style=&quot;font-size: 2rem; margin-bottom: 12px;&quot;&gt;</span><span class="si">${</span><span class="nx">defaults</span><span class="p">.</span><span class="nx">heading</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;Call to Action&#39;</span><span class="si">}</span><span class="sb">&lt;/h2&gt;</span>
</span><span id="__span-11-5"><a id="__codelineno-11-5" name="__codelineno-11-5" href="#__codelineno-11-5"></a><span class="sb"> &lt;p style=&quot;font-size: 1.1rem; margin-bottom: 24px; opacity: 0.9;&quot;&gt;</span><span class="si">${</span><span class="nx">defaults</span><span class="p">.</span><span class="nx">description</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;Description here&#39;</span><span class="si">}</span><span class="sb">&lt;/p&gt;</span>
</span><span id="__span-11-6"><a id="__codelineno-11-6" name="__codelineno-11-6" href="#__codelineno-11-6"></a><span class="sb"> &lt;a href=&quot;</span><span class="si">${</span><span class="nx">defaults</span><span class="p">.</span><span class="nx">buttonUrl</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;#&#39;</span><span class="si">}</span><span class="sb">&quot; style=&quot;display: inline-block; padding: 12px 32px; background: #fff; color: #9d4edd; text-decoration: none; border-radius: 6px; font-weight: 600;&quot;&gt;</span><span class="si">${</span><span class="nx">defaults</span><span class="p">.</span><span class="nx">buttonText</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;Click Here&#39;</span><span class="si">}</span><span class="sb">&lt;/a&gt;</span>
</span><span id="__span-11-7"><a id="__codelineno-11-7" name="__codelineno-11-7" href="#__codelineno-11-7"></a><span class="sb"> &lt;/section&gt;`</span><span class="p">;</span>
</span></code></pre></div>
<p><strong>5. Video Block</strong></p>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-12-1"><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a><span class="k">case</span><span class="w"> </span><span class="s1">&#39;video&#39;</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-12-2"><a id="__codelineno-12-2" name="__codelineno-12-2" href="#__codelineno-12-2"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">videoId</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">defaults</span><span class="p">.</span><span class="nx">videoId</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;PLACEHOLDER&#39;</span><span class="p">;</span>
</span><span id="__span-12-3"><a id="__codelineno-12-3" name="__codelineno-12-3" href="#__codelineno-12-3"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">playerType</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">defaults</span><span class="p">.</span><span class="nx">playerType</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;standard&#39;</span><span class="p">;</span>
</span><span id="__span-12-4"><a id="__codelineno-12-4" name="__codelineno-12-4" href="#__codelineno-12-4"></a>
</span><span id="__span-12-5"><a id="__codelineno-12-5" name="__codelineno-12-5" href="#__codelineno-12-5"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="sb">`</span>
</span><span id="__span-12-6"><a id="__codelineno-12-6" name="__codelineno-12-6" href="#__codelineno-12-6"></a><span class="sb"> &lt;section style=&quot;padding: 60px 40px;&quot;&gt;</span>
</span><span id="__span-12-7"><a id="__codelineno-12-7" name="__codelineno-12-7" href="#__codelineno-12-7"></a><span class="sb"> &lt;div class=&quot;video-block&quot;</span>
</span><span id="__span-12-8"><a id="__codelineno-12-8" name="__codelineno-12-8" href="#__codelineno-12-8"></a><span class="sb"> data-video-id=&quot;</span><span class="si">${</span><span class="nx">videoId</span><span class="si">}</span><span class="sb">&quot;</span>
</span><span id="__span-12-9"><a id="__codelineno-12-9" name="__codelineno-12-9" href="#__codelineno-12-9"></a><span class="sb"> data-player-type=&quot;</span><span class="si">${</span><span class="nx">playerType</span><span class="si">}</span><span class="sb">&quot;</span>
</span><span id="__span-12-10"><a id="__codelineno-12-10" name="__codelineno-12-10" href="#__codelineno-12-10"></a><span class="sb"> data-autoplay=&quot;</span><span class="si">${</span><span class="nx">defaults</span><span class="p">.</span><span class="nx">autoplay</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="kc">false</span><span class="si">}</span><span class="sb">&quot;</span>
</span><span id="__span-12-11"><a id="__codelineno-12-11" name="__codelineno-12-11" href="#__codelineno-12-11"></a><span class="sb"> data-controls=&quot;</span><span class="si">${</span><span class="nx">defaults</span><span class="p">.</span><span class="nx">controls</span><span class="w"> </span><span class="o">!==</span><span class="w"> </span><span class="kc">false</span><span class="si">}</span><span class="sb">&quot;</span>
</span><span id="__span-12-12"><a id="__codelineno-12-12" name="__codelineno-12-12" href="#__codelineno-12-12"></a><span class="sb"> data-show-reactions=&quot;</span><span class="si">${</span><span class="nx">defaults</span><span class="p">.</span><span class="nx">showReactions</span><span class="w"> </span><span class="o">!==</span><span class="w"> </span><span class="kc">false</span><span class="si">}</span><span class="sb">&quot;</span>
</span><span id="__span-12-13"><a id="__codelineno-12-13" name="__codelineno-12-13" href="#__codelineno-12-13"></a><span class="sb"> style=&quot;max-width: 100%; margin: 0 auto;&quot;&gt;</span>
</span><span id="__span-12-14"><a id="__codelineno-12-14" name="__codelineno-12-14" href="#__codelineno-12-14"></a><span class="sb"> &lt;div class=&quot;video-placeholder&quot; style=&quot;aspect-ratio: 16/9; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 12px; display: flex; align-items: center; justify-content: center;&quot;&gt;</span>
</span><span id="__span-12-15"><a id="__codelineno-12-15" name="__codelineno-12-15" href="#__codelineno-12-15"></a><span class="sb"> &lt;div style=&quot;text-align: center; color: #fff; padding: 24px;&quot;&gt;</span>
</span><span id="__span-12-16"><a id="__codelineno-12-16" name="__codelineno-12-16" href="#__codelineno-12-16"></a><span class="sb"> &lt;svg style=&quot;width: 64px; height: 64px; margin-bottom: 16px; opacity: 0.9;&quot; fill=&quot;currentColor&quot; viewBox=&quot;0 0 20 20&quot;&gt;</span>
</span><span id="__span-12-17"><a id="__codelineno-12-17" name="__codelineno-12-17" href="#__codelineno-12-17"></a><span class="sb"> &lt;path d=&quot;M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z&quot; /&gt;</span>
</span><span id="__span-12-18"><a id="__codelineno-12-18" name="__codelineno-12-18" href="#__codelineno-12-18"></a><span class="sb"> &lt;/svg&gt;</span>
</span><span id="__span-12-19"><a id="__codelineno-12-19" name="__codelineno-12-19" href="#__codelineno-12-19"></a><span class="sb"> &lt;p style=&quot;margin: 0; font-size: 1.1rem; font-weight: 600;&quot;&gt;Video Player&lt;/p&gt;</span>
</span><span id="__span-12-20"><a id="__codelineno-12-20" name="__codelineno-12-20" href="#__codelineno-12-20"></a><span class="sb"> &lt;p style=&quot;margin: 8px 0 0; font-size: 0.9rem; opacity: 0.8;&quot;&gt;ID: </span><span class="si">${</span><span class="nx">videoId</span><span class="si">}</span><span class="sb">&lt;/p&gt;</span>
</span><span id="__span-12-21"><a id="__codelineno-12-21" name="__codelineno-12-21" href="#__codelineno-12-21"></a><span class="sb"> &lt;p style=&quot;margin: 4px 0 0; font-size: 0.85rem; opacity: 0.7;&quot;&gt;</span><span class="si">${</span><span class="nx">playerType</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s1">&#39;advanced&#39;</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="s1">&#39;Advanced Player (with reactions)&#39;</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Standard HTML5 Player&#39;</span><span class="si">}</span><span class="sb">&lt;/p&gt;</span>
</span><span id="__span-12-22"><a id="__codelineno-12-22" name="__codelineno-12-22" href="#__codelineno-12-22"></a><span class="sb"> &lt;p style=&quot;margin: 12px 0 0; font-size: 0.75rem; opacity: 0.6; font-style: italic;&quot;&gt;Video will render on published page&lt;/p&gt;</span>
</span><span id="__span-12-23"><a id="__codelineno-12-23" name="__codelineno-12-23" href="#__codelineno-12-23"></a><span class="sb"> &lt;/div&gt;</span>
</span><span id="__span-12-24"><a id="__codelineno-12-24" name="__codelineno-12-24" href="#__codelineno-12-24"></a><span class="sb"> &lt;/div&gt;</span>
</span><span id="__span-12-25"><a id="__codelineno-12-25" name="__codelineno-12-25" href="#__codelineno-12-25"></a><span class="sb"> &lt;/div&gt;</span>
</span><span id="__span-12-26"><a id="__codelineno-12-26" name="__codelineno-12-26" href="#__codelineno-12-26"></a><span class="sb"> &lt;/section&gt;`</span><span class="p">;</span>
</span><span id="__span-12-27"><a id="__codelineno-12-27" name="__codelineno-12-27" href="#__codelineno-12-27"></a><span class="p">}</span>
</span></code></pre></div>
<hr />
<h2 id="save-command-integration">Save Command Integration<a class="headerlink" href="#save-command-integration" title="Permanent link">&para;</a></h2>
<h3 id="command-registration">Command Registration<a class="headerlink" href="#command-registration" title="Permanent link">&para;</a></h3>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-13-1"><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a><span class="c1">// In useEffect() after editor init</span>
</span><span id="__span-13-2"><a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a><span class="nx">editor</span><span class="p">.</span><span class="nx">Commands</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="s1">&#39;save-page&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-13-3"><a id="__codelineno-13-3" name="__codelineno-13-3" href="#__codelineno-13-3"></a><span class="w"> </span><span class="nx">run</span><span class="p">(</span><span class="nx">ed</span><span class="o">:</span><span class="w"> </span><span class="kt">Editor</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-13-4"><a id="__codelineno-13-4" name="__codelineno-13-4" href="#__codelineno-13-4"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">projectData</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">ed</span><span class="p">.</span><span class="nx">getProjectData</span><span class="p">()</span><span class="w"> </span><span class="kr">as</span><span class="w"> </span><span class="nx">Record</span><span class="o">&lt;</span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">unknown</span><span class="o">&gt;</span><span class="p">;</span>
</span><span id="__span-13-5"><a id="__codelineno-13-5" name="__codelineno-13-5" href="#__codelineno-13-5"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">html</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">ed</span><span class="p">.</span><span class="nx">getHtml</span><span class="p">();</span>
</span><span id="__span-13-6"><a id="__codelineno-13-6" name="__codelineno-13-6" href="#__codelineno-13-6"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">css</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">ed</span><span class="p">.</span><span class="nx">getCss</span><span class="p">()</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="p">;</span>
</span><span id="__span-13-7"><a id="__codelineno-13-7" name="__codelineno-13-7" href="#__codelineno-13-7"></a><span class="w"> </span><span class="nx">onSaveRef</span><span class="p">.</span><span class="nx">current</span><span class="p">({</span><span class="w"> </span><span class="nx">projectData</span><span class="p">,</span><span class="w"> </span><span class="nx">html</span><span class="p">,</span><span class="w"> </span><span class="nx">css</span><span class="w"> </span><span class="p">});</span>
</span><span id="__span-13-8"><a id="__codelineno-13-8" name="__codelineno-13-8" href="#__codelineno-13-8"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-13-9"><a id="__codelineno-13-9" name="__codelineno-13-9" href="#__codelineno-13-9"></a><span class="p">});</span>
</span></code></pre></div>
<p><strong>Why <code>onSaveRef</code>?</strong></p>
<ul>
<li>Avoids stale closure over <code>onSave</code> prop</li>
<li>Parent can update callback without re-initializing editor</li>
<li>Pattern: <code>const onSaveRef = useRef(onSave); onSaveRef.current = onSave;</code></li>
</ul>
<h3 id="keyboard-shortcut">Keyboard Shortcut<a class="headerlink" href="#keyboard-shortcut" title="Permanent link">&para;</a></h3>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-14-1"><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">handleKeyDown</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="o">:</span><span class="w"> </span><span class="kt">KeyboardEvent</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-14-2"><a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">((</span><span class="nx">e</span><span class="p">.</span><span class="nx">ctrlKey</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">metaKey</span><span class="p">)</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">key</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s1">&#39;s&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-14-3"><a id="__codelineno-14-3" name="__codelineno-14-3" href="#__codelineno-14-3"></a><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
</span><span id="__span-14-4"><a id="__codelineno-14-4" name="__codelineno-14-4" href="#__codelineno-14-4"></a><span class="w"> </span><span class="nx">editor</span><span class="p">.</span><span class="nx">runCommand</span><span class="p">(</span><span class="s1">&#39;save-page&#39;</span><span class="p">);</span>
</span><span id="__span-14-5"><a id="__codelineno-14-5" name="__codelineno-14-5" href="#__codelineno-14-5"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-14-6"><a id="__codelineno-14-6" name="__codelineno-14-6" href="#__codelineno-14-6"></a><span class="p">};</span>
</span><span id="__span-14-7"><a id="__codelineno-14-7" name="__codelineno-14-7" href="#__codelineno-14-7"></a>
</span><span id="__span-14-8"><a id="__codelineno-14-8" name="__codelineno-14-8" href="#__codelineno-14-8"></a><span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;keydown&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">handleKeyDown</span><span class="p">);</span>
</span><span id="__span-14-9"><a id="__codelineno-14-9" name="__codelineno-14-9" href="#__codelineno-14-9"></a>
</span><span id="__span-14-10"><a id="__codelineno-14-10" name="__codelineno-14-10" href="#__codelineno-14-10"></a><span class="c1">// Cleanup</span>
</span><span id="__span-14-11"><a id="__codelineno-14-11" name="__codelineno-14-11" href="#__codelineno-14-11"></a><span class="k">return</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-14-12"><a id="__codelineno-14-12" name="__codelineno-14-12" href="#__codelineno-14-12"></a><span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">removeEventListener</span><span class="p">(</span><span class="s1">&#39;keydown&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">handleKeyDown</span><span class="p">);</span>
</span><span id="__span-14-13"><a id="__codelineno-14-13" name="__codelineno-14-13" href="#__codelineno-14-13"></a><span class="w"> </span><span class="nx">editor</span><span class="p">.</span><span class="nx">destroy</span><span class="p">();</span>
</span><span id="__span-14-14"><a id="__codelineno-14-14" name="__codelineno-14-14" href="#__codelineno-14-14"></a><span class="p">};</span>
</span></code></pre></div>
<p><strong>Shortcuts:</strong></p>
<ul>
<li><strong>Windows/Linux</strong>: <code>Ctrl+S</code></li>
<li><strong>macOS</strong>: <code>Cmd+S</code></li>
</ul>
<p><strong>Behavior:</strong></p>
<ul>
<li>Prevents browser's default "Save Page As..." dialog</li>
<li>Triggers GrapesJS save command</li>
<li>Calls <code>onSave</code> callback with current state</li>
</ul>
<hr />
<h2 id="forwardref-pattern">forwardRef Pattern<a class="headerlink" href="#forwardref-pattern" title="Permanent link">&para;</a></h2>
<h3 id="implementation">Implementation<a class="headerlink" href="#implementation" title="Permanent link">&para;</a></h3>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-15-1"><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">GrapesJSEditor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">forwardRef</span><span class="o">&lt;</span><span class="nx">GrapesJSEditorHandle</span><span class="p">,</span><span class="w"> </span><span class="nx">GrapesJSEditorProps</span><span class="o">&gt;</span><span class="p">(</span>
</span><span id="__span-15-2"><a id="__codelineno-15-2" name="__codelineno-15-2" href="#__codelineno-15-2"></a><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="nx">GrapesJSEditor</span><span class="p">({</span><span class="w"> </span><span class="nx">initialData</span><span class="p">,</span><span class="w"> </span><span class="nx">onSave</span><span class="p">,</span><span class="w"> </span><span class="nx">customBlocks</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">ref</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-15-3"><a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">editorRef</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">useRef</span><span class="o">&lt;</span><span class="nx">Editor</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kc">null</span><span class="o">&gt;</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span><span id="__span-15-4"><a id="__codelineno-15-4" name="__codelineno-15-4" href="#__codelineno-15-4"></a>
</span><span id="__span-15-5"><a id="__codelineno-15-5" name="__codelineno-15-5" href="#__codelineno-15-5"></a><span class="w"> </span><span class="nx">useImperativeHandle</span><span class="p">(</span><span class="nx">ref</span><span class="p">,</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">({</span>
</span><span id="__span-15-6"><a id="__codelineno-15-6" name="__codelineno-15-6" href="#__codelineno-15-6"></a><span class="w"> </span><span class="nx">triggerSave</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-15-7"><a id="__codelineno-15-7" name="__codelineno-15-7" href="#__codelineno-15-7"></a><span class="w"> </span><span class="nx">editorRef</span><span class="p">.</span><span class="nx">current</span><span class="o">?</span><span class="p">.</span><span class="nx">runCommand</span><span class="p">(</span><span class="s1">&#39;save-page&#39;</span><span class="p">);</span>
</span><span id="__span-15-8"><a id="__codelineno-15-8" name="__codelineno-15-8" href="#__codelineno-15-8"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-15-9"><a id="__codelineno-15-9" name="__codelineno-15-9" href="#__codelineno-15-9"></a><span class="w"> </span><span class="p">}));</span>
</span><span id="__span-15-10"><a id="__codelineno-15-10" name="__codelineno-15-10" href="#__codelineno-15-10"></a>
</span><span id="__span-15-11"><a id="__codelineno-15-11" name="__codelineno-15-11" href="#__codelineno-15-11"></a><span class="w"> </span><span class="c1">// ... rest of component</span>
</span><span id="__span-15-12"><a id="__codelineno-15-12" name="__codelineno-15-12" href="#__codelineno-15-12"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-15-13"><a id="__codelineno-15-13" name="__codelineno-15-13" href="#__codelineno-15-13"></a><span class="p">);</span>
</span></code></pre></div>
<h3 id="parent-usage">Parent Usage<a class="headerlink" href="#parent-usage" title="Permanent link">&para;</a></h3>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-16-1"><a id="__codelineno-16-1" name="__codelineno-16-1" href="#__codelineno-16-1"></a><span class="c1">// In LandingPageEditor.tsx</span>
</span><span id="__span-16-2"><a id="__codelineno-16-2" name="__codelineno-16-2" href="#__codelineno-16-2"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">useRef</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span><span id="__span-16-3"><a id="__codelineno-16-3" name="__codelineno-16-3" href="#__codelineno-16-3"></a>
</span><span id="__span-16-4"><a id="__codelineno-16-4" name="__codelineno-16-4" href="#__codelineno-16-4"></a><span class="kd">const</span><span class="w"> </span><span class="nx">editorRef</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">useRef</span><span class="o">&lt;</span><span class="nx">GrapesJSEditorHandle</span><span class="o">&gt;</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span><span id="__span-16-5"><a id="__codelineno-16-5" name="__codelineno-16-5" href="#__codelineno-16-5"></a>
</span><span id="__span-16-6"><a id="__codelineno-16-6" name="__codelineno-16-6" href="#__codelineno-16-6"></a><span class="kd">const</span><span class="w"> </span><span class="nx">handleManualSave</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-16-7"><a id="__codelineno-16-7" name="__codelineno-16-7" href="#__codelineno-16-7"></a><span class="w"> </span><span class="nx">editorRef</span><span class="p">.</span><span class="nx">current</span><span class="o">?</span><span class="p">.</span><span class="nx">triggerSave</span><span class="p">();</span><span class="w"> </span><span class="c1">// Programmatic save</span>
</span><span id="__span-16-8"><a id="__codelineno-16-8" name="__codelineno-16-8" href="#__codelineno-16-8"></a><span class="p">};</span>
</span><span id="__span-16-9"><a id="__codelineno-16-9" name="__codelineno-16-9" href="#__codelineno-16-9"></a>
</span><span id="__span-16-10"><a id="__codelineno-16-10" name="__codelineno-16-10" href="#__codelineno-16-10"></a><span class="k">return</span><span class="w"> </span><span class="p">(</span>
</span><span id="__span-16-11"><a id="__codelineno-16-11" name="__codelineno-16-11" href="#__codelineno-16-11"></a><span class="w"> </span><span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span>
</span><span id="__span-16-12"><a id="__codelineno-16-12" name="__codelineno-16-12" href="#__codelineno-16-12"></a><span class="w"> </span><span class="o">&lt;</span><span class="nx">button</span><span class="w"> </span><span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="nx">handleManualSave</span><span class="p">}</span><span class="o">&gt;</span><span class="nx">Save</span><span class="o">&lt;</span><span class="err">/button&gt;</span>
</span><span id="__span-16-13"><a id="__codelineno-16-13" name="__codelineno-16-13" href="#__codelineno-16-13"></a><span class="w"> </span><span class="o">&lt;</span><span class="nx">GrapesJSEditor</span><span class="w"> </span><span class="nx">ref</span><span class="o">=</span><span class="p">{</span><span class="nx">editorRef</span><span class="p">}</span><span class="w"> </span><span class="nx">onSave</span><span class="o">=</span><span class="p">{</span><span class="nx">handleSave</span><span class="p">}</span><span class="w"> </span><span class="o">/&gt;</span>
</span><span id="__span-16-14"><a id="__codelineno-16-14" name="__codelineno-16-14" href="#__codelineno-16-14"></a><span class="w"> </span><span class="o">&lt;</span><span class="err">/div&gt;</span>
</span><span id="__span-16-15"><a id="__codelineno-16-15" name="__codelineno-16-15" href="#__codelineno-16-15"></a><span class="p">);</span>
</span></code></pre></div>
<p><strong>Why forwardRef?</strong></p>
<ul>
<li>Decouples save trigger from GrapesJS internals</li>
<li>Parent controls when to save (toolbar button, auto-save timer, etc.)</li>
<li>Cleaner API than prop drilling <code>onManualSave</code> callback</li>
</ul>
<hr />
<h2 id="error-handling">Error Handling<a class="headerlink" href="#error-handling" title="Permanent link">&para;</a></h2>
<h3 id="error-boundary-state">Error Boundary State<a class="headerlink" href="#error-boundary-state" title="Permanent link">&para;</a></h3>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-17-1"><a id="__codelineno-17-1" name="__codelineno-17-1" href="#__codelineno-17-1"></a><span class="kd">const</span><span class="w"> </span><span class="p">[</span><span class="nx">error</span><span class="p">,</span><span class="w"> </span><span class="nx">setError</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">useState</span><span class="o">&lt;</span><span class="kt">string</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kc">null</span><span class="o">&gt;</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span><span id="__span-17-2"><a id="__codelineno-17-2" name="__codelineno-17-2" href="#__codelineno-17-2"></a>
</span><span id="__span-17-3"><a id="__codelineno-17-3" name="__codelineno-17-3" href="#__codelineno-17-3"></a><span class="k">try</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-17-4"><a id="__codelineno-17-4" name="__codelineno-17-4" href="#__codelineno-17-4"></a><span class="w"> </span><span class="nx">editor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">grapesjs</span><span class="p">.</span><span class="nx">init</span><span class="p">({</span><span class="w"> </span><span class="cm">/* ... */</span><span class="w"> </span><span class="p">});</span>
</span><span id="__span-17-5"><a id="__codelineno-17-5" name="__codelineno-17-5" href="#__codelineno-17-5"></a><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-17-6"><a id="__codelineno-17-6" name="__codelineno-17-6" href="#__codelineno-17-6"></a><span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;GrapesJS init error:&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">);</span>
</span><span id="__span-17-7"><a id="__codelineno-17-7" name="__codelineno-17-7" href="#__codelineno-17-7"></a><span class="w"> </span><span class="nx">setError</span><span class="p">(</span><span class="s1">&#39;Failed to initialize the page editor. Please refresh the page.&#39;</span><span class="p">);</span>
</span><span id="__span-17-8"><a id="__codelineno-17-8" name="__codelineno-17-8" href="#__codelineno-17-8"></a><span class="w"> </span><span class="k">return</span><span class="p">;</span>
</span><span id="__span-17-9"><a id="__codelineno-17-9" name="__codelineno-17-9" href="#__codelineno-17-9"></a><span class="p">}</span>
</span><span id="__span-17-10"><a id="__codelineno-17-10" name="__codelineno-17-10" href="#__codelineno-17-10"></a>
</span><span id="__span-17-11"><a id="__codelineno-17-11" name="__codelineno-17-11" href="#__codelineno-17-11"></a><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-17-12"><a id="__codelineno-17-12" name="__codelineno-17-12" href="#__codelineno-17-12"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="p">(</span>
</span><span id="__span-17-13"><a id="__codelineno-17-13" name="__codelineno-17-13" href="#__codelineno-17-13"></a><span class="w"> </span><span class="o">&lt;</span><span class="nx">div</span><span class="w"> </span><span class="nx">style</span><span class="o">=</span><span class="p">{{</span><span class="w"> </span><span class="nx">flex</span><span class="o">:</span><span class="w"> </span><span class="kt">1</span><span class="p">,</span><span class="w"> </span><span class="nx">display</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;flex&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">alignItems</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;center&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">justifyContent</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;center&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">color</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;#ff4d4f&#39;</span><span class="w"> </span><span class="p">}}</span><span class="o">&gt;</span>
</span><span id="__span-17-14"><a id="__codelineno-17-14" name="__codelineno-17-14" href="#__codelineno-17-14"></a><span class="w"> </span><span class="p">{</span><span class="nx">error</span><span class="p">}</span>
</span><span id="__span-17-15"><a id="__codelineno-17-15" name="__codelineno-17-15" href="#__codelineno-17-15"></a><span class="w"> </span><span class="o">&lt;</span><span class="err">/div&gt;</span>
</span><span id="__span-17-16"><a id="__codelineno-17-16" name="__codelineno-17-16" href="#__codelineno-17-16"></a><span class="w"> </span><span class="p">);</span>
</span><span id="__span-17-17"><a id="__codelineno-17-17" name="__codelineno-17-17" href="#__codelineno-17-17"></a><span class="p">}</span>
</span></code></pre></div>
<p><strong>Failure Modes:</strong></p>
<ol>
<li><strong>Missing plugin:</strong> GrapesJS throws error during <code>init()</code></li>
<li><strong>Browser incompatibility:</strong> Old browser doesn't support ES6 modules</li>
<li><strong>Memory exhaustion:</strong> Very large <code>initialData</code> crashes tab</li>
</ol>
<p><strong>Recovery:</strong></p>
<ul>
<li>Error state shows user-friendly message</li>
<li>No infinite re-render (error doesn't trigger re-init)</li>
<li>User can refresh page or report issue</li>
</ul>
<h3 id="parent-level-fallback">Parent-Level Fallback<a class="headerlink" href="#parent-level-fallback" title="Permanent link">&para;</a></h3>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-18-1"><a id="__codelineno-18-1" name="__codelineno-18-1" href="#__codelineno-18-1"></a><span class="c1">// In LandingPageEditor.tsx</span>
</span><span id="__span-18-2"><a id="__codelineno-18-2" name="__codelineno-18-2" href="#__codelineno-18-2"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">ErrorBoundary</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;react-error-boundary&#39;</span><span class="p">;</span>
</span><span id="__span-18-3"><a id="__codelineno-18-3" name="__codelineno-18-3" href="#__codelineno-18-3"></a>
</span><span id="__span-18-4"><a id="__codelineno-18-4" name="__codelineno-18-4" href="#__codelineno-18-4"></a><span class="o">&lt;</span><span class="nx">ErrorBoundary</span>
</span><span id="__span-18-5"><a id="__codelineno-18-5" name="__codelineno-18-5" href="#__codelineno-18-5"></a><span class="w"> </span><span class="nx">fallback</span><span class="o">=</span><span class="p">{</span><span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span><span class="nx">Editor</span><span class="w"> </span><span class="nx">failed</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">load</span><span class="p">.</span><span class="w"> </span><span class="nx">Please</span><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="nx">CODE</span><span class="w"> </span><span class="nx">mode</span><span class="p">.</span><span class="o">&lt;</span><span class="err">/div&gt;}</span>
</span><span id="__span-18-6"><a id="__codelineno-18-6" name="__codelineno-18-6" href="#__codelineno-18-6"></a><span class="w"> </span><span class="nx">onReset</span><span class="o">=</span><span class="p">{()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">navigate</span><span class="p">(</span><span class="s1">&#39;/app/pages&#39;</span><span class="p">)}</span>
</span><span id="__span-18-7"><a id="__codelineno-18-7" name="__codelineno-18-7" href="#__codelineno-18-7"></a><span class="o">&gt;</span>
</span><span id="__span-18-8"><a id="__codelineno-18-8" name="__codelineno-18-8" href="#__codelineno-18-8"></a><span class="w"> </span><span class="o">&lt;</span><span class="nx">GrapesJSEditor</span><span class="w"> </span><span class="nx">ref</span><span class="o">=</span><span class="p">{</span><span class="nx">editorRef</span><span class="p">}</span><span class="w"> </span><span class="nx">onSave</span><span class="o">=</span><span class="p">{</span><span class="nx">handleSave</span><span class="p">}</span><span class="w"> </span><span class="o">/&gt;</span>
</span><span id="__span-18-9"><a id="__codelineno-18-9" name="__codelineno-18-9" href="#__codelineno-18-9"></a><span class="o">&lt;</span><span class="err">/ErrorBoundary&gt;</span>
</span></code></pre></div>
<p><strong>Cascade:</strong></p>
<ol>
<li>GrapesJS init error → Internal error state</li>
<li>React render error → ErrorBoundary catches</li>
<li>User sees fallback → Can switch to CODE mode</li>
</ol>
<hr />
<h2 id="mobile-detection">Mobile Detection<a class="headerlink" href="#mobile-detection" title="Permanent link">&para;</a></h2>
<h3 id="desktop-only-warning">Desktop-Only Warning<a class="headerlink" href="#desktop-only-warning" title="Permanent link">&para;</a></h3>
<p><strong>Location:</strong> <code>LandingPageEditor.tsx</code> (parent component)</p>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-19-1"><a id="__codelineno-19-1" name="__codelineno-19-1" href="#__codelineno-19-1"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">Grid</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;antd&#39;</span><span class="p">;</span>
</span><span id="__span-19-2"><a id="__codelineno-19-2" name="__codelineno-19-2" href="#__codelineno-19-2"></a>
</span><span id="__span-19-3"><a id="__codelineno-19-3" name="__codelineno-19-3" href="#__codelineno-19-3"></a><span class="kd">const</span><span class="w"> </span><span class="nx">screens</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">Grid</span><span class="p">.</span><span class="nx">useBreakpoint</span><span class="p">();</span>
</span><span id="__span-19-4"><a id="__codelineno-19-4" name="__codelineno-19-4" href="#__codelineno-19-4"></a><span class="kd">const</span><span class="w"> </span><span class="nx">isMobile</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">!</span><span class="nx">screens</span><span class="p">.</span><span class="nx">md</span><span class="p">;</span><span class="w"> </span><span class="c1">// md = 768px</span>
</span><span id="__span-19-5"><a id="__codelineno-19-5" name="__codelineno-19-5" href="#__codelineno-19-5"></a>
</span><span id="__span-19-6"><a id="__codelineno-19-6" name="__codelineno-19-6" href="#__codelineno-19-6"></a><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">isMobile</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-19-7"><a id="__codelineno-19-7" name="__codelineno-19-7" href="#__codelineno-19-7"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="p">(</span>
</span><span id="__span-19-8"><a id="__codelineno-19-8" name="__codelineno-19-8" href="#__codelineno-19-8"></a><span class="w"> </span><span class="o">&lt;</span><span class="nx">Result</span>
</span><span id="__span-19-9"><a id="__codelineno-19-9" name="__codelineno-19-9" href="#__codelineno-19-9"></a><span class="w"> </span><span class="nx">status</span><span class="o">=</span><span class="s2">&quot;warning&quot;</span>
</span><span id="__span-19-10"><a id="__codelineno-19-10" name="__codelineno-19-10" href="#__codelineno-19-10"></a><span class="w"> </span><span class="nx">title</span><span class="o">=</span><span class="s2">&quot;Desktop Required&quot;</span>
</span><span id="__span-19-11"><a id="__codelineno-19-11" name="__codelineno-19-11" href="#__codelineno-19-11"></a><span class="w"> </span><span class="nx">subTitle</span><span class="o">=</span><span class="s2">&quot;The page editor requires a desktop or tablet device (minimum 768px width).&quot;</span>
</span><span id="__span-19-12"><a id="__codelineno-19-12" name="__codelineno-19-12" href="#__codelineno-19-12"></a><span class="w"> </span><span class="nx">extra</span><span class="o">=</span><span class="p">{</span><span class="o">&lt;</span><span class="nx">Button</span><span class="w"> </span><span class="nx">onClick</span><span class="o">=</span><span class="p">{()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">navigate</span><span class="p">(</span><span class="s1">&#39;/app/pages&#39;</span><span class="p">)}</span><span class="o">&gt;</span><span class="nx">Back</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">Pages</span><span class="o">&lt;</span><span class="err">/Button&gt;}</span>
</span><span id="__span-19-13"><a id="__codelineno-19-13" name="__codelineno-19-13" href="#__codelineno-19-13"></a><span class="w"> </span><span class="err">/&gt;</span>
</span><span id="__span-19-14"><a id="__codelineno-19-14" name="__codelineno-19-14" href="#__codelineno-19-14"></a><span class="w"> </span><span class="p">);</span>
</span><span id="__span-19-15"><a id="__codelineno-19-15" name="__codelineno-19-15" href="#__codelineno-19-15"></a><span class="p">}</span>
</span></code></pre></div>
<p><strong>Why desktop-only?</strong></p>
<ul>
<li>GrapesJS drag-and-drop requires precise mouse interactions</li>
<li>Small screens can't fit 3-panel layout (blocks, canvas, properties)</li>
<li>Touch support experimental (grapesjs-touch plugin unstable)</li>
</ul>
<p><strong>Alternative for mobile admins:</strong></p>
<ul>
<li>Use CODE mode (Monaco editor works on mobile)</li>
<li>Edit on desktop, preview on mobile</li>
<li>Use responsive design testing tools</li>
</ul>
<hr />
<h2 id="data-flow-patterns">Data Flow Patterns<a class="headerlink" href="#data-flow-patterns" title="Permanent link">&para;</a></h2>
<h3 id="initial-load">Initial Load<a class="headerlink" href="#initial-load" title="Permanent link">&para;</a></h3>
<pre class="mermaid"><code>sequenceDiagram
participant DB as Database
participant API as API Service
participant Parent as LandingPageEditor
participant Editor as GrapesJSEditor
participant GJS as GrapesJS
Parent-&gt;&gt;API: GET /api/pages/:id
API-&gt;&gt;DB: SELECT blocks FROM landing_pages
DB--&gt;&gt;API: { blocks: {...} }
API--&gt;&gt;Parent: LandingPage JSON
Parent-&gt;&gt;Editor: &lt;GrapesJSEditor initialData={page.blocks} /&gt;
Editor-&gt;&gt;GJS: editor.loadProjectData(initialData)
GJS--&gt;&gt;Editor: Canvas rendered</code></pre>
<p><strong>Key Points:</strong></p>
<ul>
<li><code>blocks</code> field contains full GrapesJS <code>projectData</code> (components tree, styles, assets)</li>
<li>Empty object <code>{}</code> for new pages (GrapesJS shows blank canvas)</li>
<li>Large JSON (50KB+) loads in ~200ms</li>
</ul>
<h3 id="save-flow">Save Flow<a class="headerlink" href="#save-flow" title="Permanent link">&para;</a></h3>
<pre class="mermaid"><code>sequenceDiagram
participant User as User
participant Parent as LandingPageEditor
participant Editor as GrapesJSEditor
participant GJS as GrapesJS
participant API as API
User-&gt;&gt;User: Press Ctrl+S
User-&gt;&gt;Editor: KeyboardEvent
Editor-&gt;&gt;GJS: runCommand('save-page')
GJS-&gt;&gt;GJS: getProjectData()
GJS-&gt;&gt;GJS: getHtml()
GJS-&gt;&gt;GJS: getCss()
GJS--&gt;&gt;Editor: { projectData, html, css }
Editor-&gt;&gt;Parent: onSave(data)
Parent-&gt;&gt;API: PUT /api/pages/:id
API--&gt;&gt;Parent: 200 OK
Parent-&gt;&gt;User: "Page saved" notification</code></pre>
<p><strong>Critical Detail:</strong></p>
<ul>
<li><code>getProjectData()</code> returns full editor state (for future edits)</li>
<li><code>getHtml()</code> returns rendered HTML (for public display)</li>
<li><code>getCss()</code> returns compiled CSS (for public display)</li>
<li>All three saved to database (different use cases)</li>
</ul>
<hr />
<h2 id="code-examples">Code Examples<a class="headerlink" href="#code-examples" title="Permanent link">&para;</a></h2>
<h3 id="complete-integration-example">Complete Integration Example<a class="headerlink" href="#complete-integration-example" title="Permanent link">&para;</a></h3>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-20-1"><a id="__codelineno-20-1" name="__codelineno-20-1" href="#__codelineno-20-1"></a><span class="c1">// admin/src/pages/LandingPageEditor.tsx</span>
</span><span id="__span-20-2"><a id="__codelineno-20-2" name="__codelineno-20-2" href="#__codelineno-20-2"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">useState</span><span class="p">,</span><span class="w"> </span><span class="nx">useEffect</span><span class="p">,</span><span class="w"> </span><span class="nx">useRef</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span><span id="__span-20-3"><a id="__codelineno-20-3" name="__codelineno-20-3" href="#__codelineno-20-3"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">useNavigate</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;react-router-dom&#39;</span><span class="p">;</span>
</span><span id="__span-20-4"><a id="__codelineno-20-4" name="__codelineno-20-4" href="#__codelineno-20-4"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">Button</span><span class="p">,</span><span class="w"> </span><span class="nx">message</span><span class="p">,</span><span class="w"> </span><span class="nx">Spin</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;antd&#39;</span><span class="p">;</span>
</span><span id="__span-20-5"><a id="__codelineno-20-5" name="__codelineno-20-5" href="#__codelineno-20-5"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">api</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;@/lib/api&#39;</span><span class="p">;</span>
</span><span id="__span-20-6"><a id="__codelineno-20-6" name="__codelineno-20-6" href="#__codelineno-20-6"></a><span class="k">import</span><span class="w"> </span><span class="nx">GrapesJSEditor</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">GrapesJSEditorHandle</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;@/components/GrapesJSEditor&#39;</span><span class="p">;</span>
</span><span id="__span-20-7"><a id="__codelineno-20-7" name="__codelineno-20-7" href="#__codelineno-20-7"></a><span class="k">import</span><span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">LandingPage</span><span class="p">,</span><span class="w"> </span><span class="nx">PageBlock</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;@/types/api&#39;</span><span class="p">;</span>
</span><span id="__span-20-8"><a id="__codelineno-20-8" name="__codelineno-20-8" href="#__codelineno-20-8"></a>
</span><span id="__span-20-9"><a id="__codelineno-20-9" name="__codelineno-20-9" href="#__codelineno-20-9"></a><span class="kd">interface</span><span class="w"> </span><span class="nx">LandingPageEditorProps</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-20-10"><a id="__codelineno-20-10" name="__codelineno-20-10" href="#__codelineno-20-10"></a><span class="w"> </span><span class="nx">pageId</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">;</span>
</span><span id="__span-20-11"><a id="__codelineno-20-11" name="__codelineno-20-11" href="#__codelineno-20-11"></a><span class="w"> </span><span class="nx">onClose</span><span class="o">:</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="ow">void</span><span class="p">;</span>
</span><span id="__span-20-12"><a id="__codelineno-20-12" name="__codelineno-20-12" href="#__codelineno-20-12"></a><span class="p">}</span>
</span><span id="__span-20-13"><a id="__codelineno-20-13" name="__codelineno-20-13" href="#__codelineno-20-13"></a>
</span><span id="__span-20-14"><a id="__codelineno-20-14" name="__codelineno-20-14" href="#__codelineno-20-14"></a><span class="k">export</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="nx">LandingPageEditor</span><span class="p">({</span><span class="w"> </span><span class="nx">pageId</span><span class="p">,</span><span class="w"> </span><span class="nx">onClose</span><span class="w"> </span><span class="p">}</span><span class="o">:</span><span class="w"> </span><span class="nx">LandingPageEditorProps</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-20-15"><a id="__codelineno-20-15" name="__codelineno-20-15" href="#__codelineno-20-15"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">navigate</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">useNavigate</span><span class="p">();</span>
</span><span id="__span-20-16"><a id="__codelineno-20-16" name="__codelineno-20-16" href="#__codelineno-20-16"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">editorRef</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">useRef</span><span class="o">&lt;</span><span class="nx">GrapesJSEditorHandle</span><span class="o">&gt;</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span><span id="__span-20-17"><a id="__codelineno-20-17" name="__codelineno-20-17" href="#__codelineno-20-17"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="p">[</span><span class="nx">page</span><span class="p">,</span><span class="w"> </span><span class="nx">setPage</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">useState</span><span class="o">&lt;</span><span class="nx">LandingPage</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kc">null</span><span class="o">&gt;</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span><span id="__span-20-18"><a id="__codelineno-20-18" name="__codelineno-20-18" href="#__codelineno-20-18"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="p">[</span><span class="nx">blocks</span><span class="p">,</span><span class="w"> </span><span class="nx">setBlocks</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">useState</span><span class="o">&lt;</span><span class="nx">PageBlock</span><span class="p">[]</span><span class="o">&gt;</span><span class="p">([]);</span>
</span><span id="__span-20-19"><a id="__codelineno-20-19" name="__codelineno-20-19" href="#__codelineno-20-19"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="p">[</span><span class="nx">loading</span><span class="p">,</span><span class="w"> </span><span class="nx">setLoading</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">useState</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
</span><span id="__span-20-20"><a id="__codelineno-20-20" name="__codelineno-20-20" href="#__codelineno-20-20"></a>
</span><span id="__span-20-21"><a id="__codelineno-20-21" name="__codelineno-20-21" href="#__codelineno-20-21"></a><span class="w"> </span><span class="nx">useEffect</span><span class="p">(()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-20-22"><a id="__codelineno-20-22" name="__codelineno-20-22" href="#__codelineno-20-22"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">fetchData</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-20-23"><a id="__codelineno-20-23" name="__codelineno-20-23" href="#__codelineno-20-23"></a><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-20-24"><a id="__codelineno-20-24" name="__codelineno-20-24" href="#__codelineno-20-24"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="p">[</span><span class="nx">pageRes</span><span class="p">,</span><span class="w"> </span><span class="nx">blocksRes</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">([</span>
</span><span id="__span-20-25"><a id="__codelineno-20-25" name="__codelineno-20-25" href="#__codelineno-20-25"></a><span class="w"> </span><span class="nx">api</span><span class="p">.</span><span class="nx">get</span><span class="o">&lt;</span><span class="nx">LandingPage</span><span class="o">&gt;</span><span class="p">(</span><span class="sb">`/pages/</span><span class="si">${</span><span class="nx">pageId</span><span class="si">}</span><span class="sb">`</span><span class="p">),</span>
</span><span id="__span-20-26"><a id="__codelineno-20-26" name="__codelineno-20-26" href="#__codelineno-20-26"></a><span class="w"> </span><span class="nx">api</span><span class="p">.</span><span class="nx">get</span><span class="o">&lt;</span><span class="nx">PageBlock</span><span class="p">[]</span><span class="o">&gt;</span><span class="p">(</span><span class="s1">&#39;/page-blocks&#39;</span><span class="p">),</span>
</span><span id="__span-20-27"><a id="__codelineno-20-27" name="__codelineno-20-27" href="#__codelineno-20-27"></a><span class="w"> </span><span class="p">]);</span>
</span><span id="__span-20-28"><a id="__codelineno-20-28" name="__codelineno-20-28" href="#__codelineno-20-28"></a><span class="w"> </span><span class="nx">setPage</span><span class="p">(</span><span class="nx">pageRes</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span>
</span><span id="__span-20-29"><a id="__codelineno-20-29" name="__codelineno-20-29" href="#__codelineno-20-29"></a><span class="w"> </span><span class="nx">setBlocks</span><span class="p">(</span><span class="nx">blocksRes</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span>
</span><span id="__span-20-30"><a id="__codelineno-20-30" name="__codelineno-20-30" href="#__codelineno-20-30"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-20-31"><a id="__codelineno-20-31" name="__codelineno-20-31" href="#__codelineno-20-31"></a><span class="w"> </span><span class="nx">message</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;Failed to load page&#39;</span><span class="p">);</span>
</span><span id="__span-20-32"><a id="__codelineno-20-32" name="__codelineno-20-32" href="#__codelineno-20-32"></a><span class="w"> </span><span class="nx">onClose</span><span class="p">();</span>
</span><span id="__span-20-33"><a id="__codelineno-20-33" name="__codelineno-20-33" href="#__codelineno-20-33"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">finally</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-20-34"><a id="__codelineno-20-34" name="__codelineno-20-34" href="#__codelineno-20-34"></a><span class="w"> </span><span class="nx">setLoading</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span>
</span><span id="__span-20-35"><a id="__codelineno-20-35" name="__codelineno-20-35" href="#__codelineno-20-35"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-20-36"><a id="__codelineno-20-36" name="__codelineno-20-36" href="#__codelineno-20-36"></a><span class="w"> </span><span class="p">};</span>
</span><span id="__span-20-37"><a id="__codelineno-20-37" name="__codelineno-20-37" href="#__codelineno-20-37"></a><span class="w"> </span><span class="nx">fetchData</span><span class="p">();</span>
</span><span id="__span-20-38"><a id="__codelineno-20-38" name="__codelineno-20-38" href="#__codelineno-20-38"></a><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span><span class="nx">pageId</span><span class="p">,</span><span class="w"> </span><span class="nx">onClose</span><span class="p">]);</span>
</span><span id="__span-20-39"><a id="__codelineno-20-39" name="__codelineno-20-39" href="#__codelineno-20-39"></a>
</span><span id="__span-20-40"><a id="__codelineno-20-40" name="__codelineno-20-40" href="#__codelineno-20-40"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">handleSave</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="p">(</span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">projectData</span><span class="o">:</span><span class="w"> </span><span class="kt">any</span><span class="p">;</span><span class="w"> </span><span class="nx">html</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">;</span><span class="w"> </span><span class="nx">css</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-20-41"><a id="__codelineno-20-41" name="__codelineno-20-41" href="#__codelineno-20-41"></a><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-20-42"><a id="__codelineno-20-42" name="__codelineno-20-42" href="#__codelineno-20-42"></a><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">api</span><span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="sb">`/pages/</span><span class="si">${</span><span class="nx">pageId</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-20-43"><a id="__codelineno-20-43" name="__codelineno-20-43" href="#__codelineno-20-43"></a><span class="w"> </span><span class="nx">blocks</span><span class="o">:</span><span class="w"> </span><span class="kt">data.projectData</span><span class="p">,</span>
</span><span id="__span-20-44"><a id="__codelineno-20-44" name="__codelineno-20-44" href="#__codelineno-20-44"></a><span class="w"> </span><span class="nx">htmlOutput</span><span class="o">:</span><span class="w"> </span><span class="kt">data.html</span><span class="p">,</span>
</span><span id="__span-20-45"><a id="__codelineno-20-45" name="__codelineno-20-45" href="#__codelineno-20-45"></a><span class="w"> </span><span class="nx">cssOutput</span><span class="o">:</span><span class="w"> </span><span class="kt">data.css</span><span class="p">,</span>
</span><span id="__span-20-46"><a id="__codelineno-20-46" name="__codelineno-20-46" href="#__codelineno-20-46"></a><span class="w"> </span><span class="p">});</span>
</span><span id="__span-20-47"><a id="__codelineno-20-47" name="__codelineno-20-47" href="#__codelineno-20-47"></a><span class="w"> </span><span class="nx">message</span><span class="p">.</span><span class="nx">success</span><span class="p">(</span><span class="s1">&#39;Page saved&#39;</span><span class="p">);</span>
</span><span id="__span-20-48"><a id="__codelineno-20-48" name="__codelineno-20-48" href="#__codelineno-20-48"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-20-49"><a id="__codelineno-20-49" name="__codelineno-20-49" href="#__codelineno-20-49"></a><span class="w"> </span><span class="nx">message</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;Failed to save page&#39;</span><span class="p">);</span>
</span><span id="__span-20-50"><a id="__codelineno-20-50" name="__codelineno-20-50" href="#__codelineno-20-50"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-20-51"><a id="__codelineno-20-51" name="__codelineno-20-51" href="#__codelineno-20-51"></a><span class="w"> </span><span class="p">};</span>
</span><span id="__span-20-52"><a id="__codelineno-20-52" name="__codelineno-20-52" href="#__codelineno-20-52"></a>
</span><span id="__span-20-53"><a id="__codelineno-20-53" name="__codelineno-20-53" href="#__codelineno-20-53"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">handleManualSave</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-20-54"><a id="__codelineno-20-54" name="__codelineno-20-54" href="#__codelineno-20-54"></a><span class="w"> </span><span class="nx">editorRef</span><span class="p">.</span><span class="nx">current</span><span class="o">?</span><span class="p">.</span><span class="nx">triggerSave</span><span class="p">();</span>
</span><span id="__span-20-55"><a id="__codelineno-20-55" name="__codelineno-20-55" href="#__codelineno-20-55"></a><span class="w"> </span><span class="p">};</span>
</span><span id="__span-20-56"><a id="__codelineno-20-56" name="__codelineno-20-56" href="#__codelineno-20-56"></a>
</span><span id="__span-20-57"><a id="__codelineno-20-57" name="__codelineno-20-57" href="#__codelineno-20-57"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">loading</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="o">&lt;</span><span class="nx">Spin</span><span class="w"> </span><span class="nx">size</span><span class="o">=</span><span class="s2">&quot;large&quot;</span><span class="w"> </span><span class="o">/&gt;</span><span class="p">;</span>
</span><span id="__span-20-58"><a id="__codelineno-20-58" name="__codelineno-20-58" href="#__codelineno-20-58"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nx">page</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">null</span><span class="p">;</span>
</span><span id="__span-20-59"><a id="__codelineno-20-59" name="__codelineno-20-59" href="#__codelineno-20-59"></a>
</span><span id="__span-20-60"><a id="__codelineno-20-60" name="__codelineno-20-60" href="#__codelineno-20-60"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="p">(</span>
</span><span id="__span-20-61"><a id="__codelineno-20-61" name="__codelineno-20-61" href="#__codelineno-20-61"></a><span class="w"> </span><span class="o">&lt;</span><span class="nx">div</span><span class="w"> </span><span class="nx">style</span><span class="o">=</span><span class="p">{{</span><span class="w"> </span><span class="nx">height</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;100vh&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">display</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;flex&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">flexDirection</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;column&#39;</span><span class="w"> </span><span class="p">}}</span><span class="o">&gt;</span>
</span><span id="__span-20-62"><a id="__codelineno-20-62" name="__codelineno-20-62" href="#__codelineno-20-62"></a><span class="w"> </span><span class="o">&lt;</span><span class="nx">div</span><span class="w"> </span><span class="nx">style</span><span class="o">=</span><span class="p">{{</span><span class="w"> </span><span class="nx">padding</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;12px 16px&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">borderBottom</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;1px solid #d9d9d9&#39;</span><span class="w"> </span><span class="p">}}</span><span class="o">&gt;</span>
</span><span id="__span-20-63"><a id="__codelineno-20-63" name="__codelineno-20-63" href="#__codelineno-20-63"></a><span class="w"> </span><span class="o">&lt;</span><span class="nx">Button</span><span class="w"> </span><span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="nx">onClose</span><span class="p">}</span><span class="o">&gt;</span><span class="nx">Back</span><span class="o">&lt;</span><span class="err">/Button&gt;</span>
</span><span id="__span-20-64"><a id="__codelineno-20-64" name="__codelineno-20-64" href="#__codelineno-20-64"></a><span class="w"> </span><span class="o">&lt;</span><span class="nx">Button</span><span class="w"> </span><span class="kr">type</span><span class="o">=</span><span class="s2">&quot;primary&quot;</span><span class="w"> </span><span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="nx">handleManualSave</span><span class="p">}</span><span class="w"> </span><span class="nx">style</span><span class="o">=</span><span class="p">{{</span><span class="w"> </span><span class="nx">marginLeft</span><span class="o">:</span><span class="w"> </span><span class="kt">8</span><span class="w"> </span><span class="p">}}</span><span class="o">&gt;</span>
</span><span id="__span-20-65"><a id="__codelineno-20-65" name="__codelineno-20-65" href="#__codelineno-20-65"></a><span class="w"> </span><span class="nx">Save</span><span class="w"> </span><span class="p">(</span><span class="nx">Ctrl</span><span class="o">+</span><span class="nx">S</span><span class="p">)</span>
</span><span id="__span-20-66"><a id="__codelineno-20-66" name="__codelineno-20-66" href="#__codelineno-20-66"></a><span class="w"> </span><span class="o">&lt;</span><span class="err">/Button&gt;</span>
</span><span id="__span-20-67"><a id="__codelineno-20-67" name="__codelineno-20-67" href="#__codelineno-20-67"></a><span class="w"> </span><span class="o">&lt;</span><span class="err">/div&gt;</span>
</span><span id="__span-20-68"><a id="__codelineno-20-68" name="__codelineno-20-68" href="#__codelineno-20-68"></a><span class="w"> </span><span class="o">&lt;</span><span class="nx">GrapesJSEditor</span>
</span><span id="__span-20-69"><a id="__codelineno-20-69" name="__codelineno-20-69" href="#__codelineno-20-69"></a><span class="w"> </span><span class="nx">ref</span><span class="o">=</span><span class="p">{</span><span class="nx">editorRef</span><span class="p">}</span>
</span><span id="__span-20-70"><a id="__codelineno-20-70" name="__codelineno-20-70" href="#__codelineno-20-70"></a><span class="w"> </span><span class="nx">initialData</span><span class="o">=</span><span class="p">{</span><span class="nx">page</span><span class="p">.</span><span class="nx">blocks</span><span class="w"> </span><span class="kr">as</span><span class="w"> </span><span class="nx">Record</span><span class="o">&lt;</span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">unknown</span><span class="o">&gt;</span><span class="p">}</span>
</span><span id="__span-20-71"><a id="__codelineno-20-71" name="__codelineno-20-71" href="#__codelineno-20-71"></a><span class="w"> </span><span class="nx">onSave</span><span class="o">=</span><span class="p">{</span><span class="nx">handleSave</span><span class="p">}</span>
</span><span id="__span-20-72"><a id="__codelineno-20-72" name="__codelineno-20-72" href="#__codelineno-20-72"></a><span class="w"> </span><span class="nx">customBlocks</span><span class="o">=</span><span class="p">{</span><span class="nx">blocks</span><span class="p">}</span>
</span><span id="__span-20-73"><a id="__codelineno-20-73" name="__codelineno-20-73" href="#__codelineno-20-73"></a><span class="w"> </span><span class="o">/&gt;</span>
</span><span id="__span-20-74"><a id="__codelineno-20-74" name="__codelineno-20-74" href="#__codelineno-20-74"></a><span class="w"> </span><span class="o">&lt;</span><span class="err">/div&gt;</span>
</span><span id="__span-20-75"><a id="__codelineno-20-75" name="__codelineno-20-75" href="#__codelineno-20-75"></a><span class="w"> </span><span class="p">);</span>
</span><span id="__span-20-76"><a id="__codelineno-20-76" name="__codelineno-20-76" href="#__codelineno-20-76"></a><span class="p">}</span>
</span></code></pre></div>
<h3 id="custom-block-registration">Custom Block Registration<a class="headerlink" href="#custom-block-registration" title="Permanent link">&para;</a></h3>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-21-1"><a id="__codelineno-21-1" name="__codelineno-21-1" href="#__codelineno-21-1"></a><span class="c1">// Add a custom &quot;Campaign Stats&quot; block</span>
</span><span id="__span-21-2"><a id="__codelineno-21-2" name="__codelineno-21-2" href="#__codelineno-21-2"></a><span class="kd">const</span><span class="w"> </span><span class="nx">campaignStatsBlock</span><span class="o">:</span><span class="w"> </span><span class="kt">PageBlock</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-21-3"><a id="__codelineno-21-3" name="__codelineno-21-3" href="#__codelineno-21-3"></a><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;custom-campaign-stats&#39;</span><span class="p">,</span>
</span><span id="__span-21-4"><a id="__codelineno-21-4" name="__codelineno-21-4" href="#__codelineno-21-4"></a><span class="w"> </span><span class="kr">type</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;campaign-stats&#39;</span><span class="p">,</span>
</span><span id="__span-21-5"><a id="__codelineno-21-5" name="__codelineno-21-5" href="#__codelineno-21-5"></a><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Campaign Stats&#39;</span><span class="p">,</span>
</span><span id="__span-21-6"><a id="__codelineno-21-6" name="__codelineno-21-6" href="#__codelineno-21-6"></a><span class="w"> </span><span class="nx">category</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Campaign&#39;</span><span class="p">,</span>
</span><span id="__span-21-7"><a id="__codelineno-21-7" name="__codelineno-21-7" href="#__codelineno-21-7"></a><span class="w"> </span><span class="nx">sortOrder</span><span class="o">:</span><span class="w"> </span><span class="kt">10</span><span class="p">,</span>
</span><span id="__span-21-8"><a id="__codelineno-21-8" name="__codelineno-21-8" href="#__codelineno-21-8"></a><span class="w"> </span><span class="nx">schema</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-21-9"><a id="__codelineno-21-9" name="__codelineno-21-9" href="#__codelineno-21-9"></a><span class="w"> </span><span class="nx">volunteers</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="kr">type</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;number&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Volunteers&#39;</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-21-10"><a id="__codelineno-21-10" name="__codelineno-21-10" href="#__codelineno-21-10"></a><span class="w"> </span><span class="nx">emails</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="kr">type</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;number&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Emails Sent&#39;</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-21-11"><a id="__codelineno-21-11" name="__codelineno-21-11" href="#__codelineno-21-11"></a><span class="w"> </span><span class="nx">events</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="kr">type</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;number&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Events&#39;</span><span class="w"> </span><span class="p">},</span>
</span><span id="__span-21-12"><a id="__codelineno-21-12" name="__codelineno-21-12" href="#__codelineno-21-12"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-21-13"><a id="__codelineno-21-13" name="__codelineno-21-13" href="#__codelineno-21-13"></a><span class="w"> </span><span class="nx">defaults</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-21-14"><a id="__codelineno-21-14" name="__codelineno-21-14" href="#__codelineno-21-14"></a><span class="w"> </span><span class="nx">volunteers</span><span class="o">:</span><span class="w"> </span><span class="kt">1250</span><span class="p">,</span>
</span><span id="__span-21-15"><a id="__codelineno-21-15" name="__codelineno-21-15" href="#__codelineno-21-15"></a><span class="w"> </span><span class="nx">emails</span><span class="o">:</span><span class="w"> </span><span class="kt">5400</span><span class="p">,</span>
</span><span id="__span-21-16"><a id="__codelineno-21-16" name="__codelineno-21-16" href="#__codelineno-21-16"></a><span class="w"> </span><span class="nx">events</span><span class="o">:</span><span class="w"> </span><span class="kt">32</span><span class="p">,</span>
</span><span id="__span-21-17"><a id="__codelineno-21-17" name="__codelineno-21-17" href="#__codelineno-21-17"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-21-18"><a id="__codelineno-21-18" name="__codelineno-21-18" href="#__codelineno-21-18"></a><span class="p">};</span>
</span><span id="__span-21-19"><a id="__codelineno-21-19" name="__codelineno-21-19" href="#__codelineno-21-19"></a>
</span><span id="__span-21-20"><a id="__codelineno-21-20" name="__codelineno-21-20" href="#__codelineno-21-20"></a><span class="c1">// GrapesJSEditor will auto-register via generateBlockHtml()</span>
</span><span id="__span-21-21"><a id="__codelineno-21-21" name="__codelineno-21-21" href="#__codelineno-21-21"></a><span class="o">&lt;</span><span class="nx">GrapesJSEditor</span><span class="w"> </span><span class="nx">customBlocks</span><span class="o">=</span><span class="p">{[</span><span class="nx">campaignStatsBlock</span><span class="p">,</span><span class="w"> </span><span class="p">...</span><span class="nx">otherBlocks</span><span class="p">]}</span><span class="w"> </span><span class="o">/&gt;</span>
</span></code></pre></div>
<h3 id="adding-custom-html-generation">Adding Custom HTML Generation<a class="headerlink" href="#adding-custom-html-generation" title="Permanent link">&para;</a></h3>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-22-1"><a id="__codelineno-22-1" name="__codelineno-22-1" href="#__codelineno-22-1"></a><span class="c1">// In GrapesJSEditor.tsx generateBlockHtml() function</span>
</span><span id="__span-22-2"><a id="__codelineno-22-2" name="__codelineno-22-2" href="#__codelineno-22-2"></a><span class="k">case</span><span class="w"> </span><span class="s1">&#39;campaign-stats&#39;</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-22-3"><a id="__codelineno-22-3" name="__codelineno-22-3" href="#__codelineno-22-3"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">volunteers</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">defaults</span><span class="p">.</span><span class="nx">volunteers</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span>
</span><span id="__span-22-4"><a id="__codelineno-22-4" name="__codelineno-22-4" href="#__codelineno-22-4"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">emails</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">defaults</span><span class="p">.</span><span class="nx">emails</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span>
</span><span id="__span-22-5"><a id="__codelineno-22-5" name="__codelineno-22-5" href="#__codelineno-22-5"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">events</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">defaults</span><span class="p">.</span><span class="nx">events</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span>
</span><span id="__span-22-6"><a id="__codelineno-22-6" name="__codelineno-22-6" href="#__codelineno-22-6"></a>
</span><span id="__span-22-7"><a id="__codelineno-22-7" name="__codelineno-22-7" href="#__codelineno-22-7"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="sb">`</span>
</span><span id="__span-22-8"><a id="__codelineno-22-8" name="__codelineno-22-8" href="#__codelineno-22-8"></a><span class="sb"> &lt;section style=&quot;padding: 60px 40px; background: #f8f9fa; text-align: center;&quot;&gt;</span>
</span><span id="__span-22-9"><a id="__codelineno-22-9" name="__codelineno-22-9" href="#__codelineno-22-9"></a><span class="sb"> &lt;h2 style=&quot;margin-bottom: 32px; font-size: 2rem;&quot;&gt;Our Impact&lt;/h2&gt;</span>
</span><span id="__span-22-10"><a id="__codelineno-22-10" name="__codelineno-22-10" href="#__codelineno-22-10"></a><span class="sb"> &lt;div style=&quot;display: flex; gap: 48px; justify-content: center; flex-wrap: wrap;&quot;&gt;</span>
</span><span id="__span-22-11"><a id="__codelineno-22-11" name="__codelineno-22-11" href="#__codelineno-22-11"></a><span class="sb"> &lt;div&gt;</span>
</span><span id="__span-22-12"><a id="__codelineno-22-12" name="__codelineno-22-12" href="#__codelineno-22-12"></a><span class="sb"> &lt;div style=&quot;font-size: 3rem; font-weight: 700; color: #9d4edd;&quot;&gt;</span><span class="si">${</span><span class="nx">volunteers</span><span class="p">.</span><span class="nx">toLocaleString</span><span class="p">()</span><span class="si">}</span><span class="sb">&lt;/div&gt;</span>
</span><span id="__span-22-13"><a id="__codelineno-22-13" name="__codelineno-22-13" href="#__codelineno-22-13"></a><span class="sb"> &lt;div style=&quot;font-size: 1rem; color: #666;&quot;&gt;Volunteers&lt;/div&gt;</span>
</span><span id="__span-22-14"><a id="__codelineno-22-14" name="__codelineno-22-14" href="#__codelineno-22-14"></a><span class="sb"> &lt;/div&gt;</span>
</span><span id="__span-22-15"><a id="__codelineno-22-15" name="__codelineno-22-15" href="#__codelineno-22-15"></a><span class="sb"> &lt;div&gt;</span>
</span><span id="__span-22-16"><a id="__codelineno-22-16" name="__codelineno-22-16" href="#__codelineno-22-16"></a><span class="sb"> &lt;div style=&quot;font-size: 3rem; font-weight: 700; color: #9d4edd;&quot;&gt;</span><span class="si">${</span><span class="nx">emails</span><span class="p">.</span><span class="nx">toLocaleString</span><span class="p">()</span><span class="si">}</span><span class="sb">&lt;/div&gt;</span>
</span><span id="__span-22-17"><a id="__codelineno-22-17" name="__codelineno-22-17" href="#__codelineno-22-17"></a><span class="sb"> &lt;div style=&quot;font-size: 1rem; color: #666;&quot;&gt;Emails Sent&lt;/div&gt;</span>
</span><span id="__span-22-18"><a id="__codelineno-22-18" name="__codelineno-22-18" href="#__codelineno-22-18"></a><span class="sb"> &lt;/div&gt;</span>
</span><span id="__span-22-19"><a id="__codelineno-22-19" name="__codelineno-22-19" href="#__codelineno-22-19"></a><span class="sb"> &lt;div&gt;</span>
</span><span id="__span-22-20"><a id="__codelineno-22-20" name="__codelineno-22-20" href="#__codelineno-22-20"></a><span class="sb"> &lt;div style=&quot;font-size: 3rem; font-weight: 700; color: #9d4edd;&quot;&gt;</span><span class="si">${</span><span class="nx">events</span><span class="si">}</span><span class="sb">&lt;/div&gt;</span>
</span><span id="__span-22-21"><a id="__codelineno-22-21" name="__codelineno-22-21" href="#__codelineno-22-21"></a><span class="sb"> &lt;div style=&quot;font-size: 1rem; color: #666;&quot;&gt;Events&lt;/div&gt;</span>
</span><span id="__span-22-22"><a id="__codelineno-22-22" name="__codelineno-22-22" href="#__codelineno-22-22"></a><span class="sb"> &lt;/div&gt;</span>
</span><span id="__span-22-23"><a id="__codelineno-22-23" name="__codelineno-22-23" href="#__codelineno-22-23"></a><span class="sb"> &lt;/div&gt;</span>
</span><span id="__span-22-24"><a id="__codelineno-22-24" name="__codelineno-22-24" href="#__codelineno-22-24"></a><span class="sb"> &lt;/section&gt;`</span><span class="p">;</span>
</span><span id="__span-22-25"><a id="__codelineno-22-25" name="__codelineno-22-25" href="#__codelineno-22-25"></a><span class="p">}</span>
</span></code></pre></div>
<hr />
<h2 id="troubleshooting">Troubleshooting<a class="headerlink" href="#troubleshooting" title="Permanent link">&para;</a></h2>
<h3 id="problem-blocks-not-appearing-in-left-panel">Problem: Blocks Not Appearing in Left Panel<a class="headerlink" href="#problem-blocks-not-appearing-in-left-panel" title="Permanent link">&para;</a></h3>
<p><strong>Symptoms:</strong></p>
<ul>
<li>Custom blocks array passed to GrapesJSEditor</li>
<li>Left panel shows default blocks only</li>
<li>No campaign-specific blocks</li>
</ul>
<p><strong>Causes:</strong></p>
<ol>
<li><code>generateBlockHtml()</code> missing case for block type</li>
<li>Category name mismatch</li>
<li>Block registration timing issue</li>
</ol>
<p><strong>Solutions:</strong></p>
<ol>
<li>
<p><strong>Add case to generateBlockHtml():</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-23-1"><a id="__codelineno-23-1" name="__codelineno-23-1" href="#__codelineno-23-1"></a><span class="k">case</span><span class="w"> </span><span class="s1">&#39;my-custom-block&#39;</span><span class="o">:</span>
</span><span id="__span-23-2"><a id="__codelineno-23-2" name="__codelineno-23-2" href="#__codelineno-23-2"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="sb">`&lt;section&gt;My custom block HTML&lt;/section&gt;`</span><span class="p">;</span>
</span></code></pre></div></p>
</li>
<li>
<p><strong>Check category:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-24-1"><a id="__codelineno-24-1" name="__codelineno-24-1" href="#__codelineno-24-1"></a><span class="c1">// Block category: &quot;Campaign&quot;</span>
</span><span id="__span-24-2"><a id="__codelineno-24-2" name="__codelineno-24-2" href="#__codelineno-24-2"></a><span class="c1">// GrapesJS shows blocks in collapsible &quot;Campaign&quot; section</span>
</span><span id="__span-24-3"><a id="__codelineno-24-3" name="__codelineno-24-3" href="#__codelineno-24-3"></a><span class="c1">// Case-sensitive match</span>
</span></code></pre></div></p>
</li>
<li>
<p><strong>Verify registration timing:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-25-1"><a id="__codelineno-25-1" name="__codelineno-25-1" href="#__codelineno-25-1"></a><span class="c1">// Registration happens in useEffect after init</span>
</span><span id="__span-25-2"><a id="__codelineno-25-2" name="__codelineno-25-2" href="#__codelineno-25-2"></a><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Registering blocks:&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">customBlocks</span><span class="p">.</span><span class="nx">length</span><span class="p">);</span>
</span></code></pre></div></p>
</li>
<li>
<p><strong>Inspect BlockManager:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-26-1"><a id="__codelineno-26-1" name="__codelineno-26-1" href="#__codelineno-26-1"></a><span class="c1">// In browser console (after editor loads)</span>
</span><span id="__span-26-2"><a id="__codelineno-26-2" name="__codelineno-26-2" href="#__codelineno-26-2"></a><span class="nb">window</span><span class="p">.</span><span class="nx">editor</span><span class="p">.</span><span class="nx">BlockManager</span><span class="p">.</span><span class="nx">getAll</span><span class="p">().</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">b</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">b</span><span class="p">.</span><span class="nx">id</span><span class="p">));</span>
</span><span id="__span-26-3"><a id="__codelineno-26-3" name="__codelineno-26-3" href="#__codelineno-26-3"></a><span class="c1">// Should include &#39;custom-hero&#39;, &#39;custom-text&#39;, etc.</span>
</span></code></pre></div></p>
</li>
</ol>
<hr />
<h3 id="problem-save-not-triggering">Problem: Save Not Triggering<a class="headerlink" href="#problem-save-not-triggering" title="Permanent link">&para;</a></h3>
<p><strong>Symptoms:</strong></p>
<ul>
<li>Press Ctrl+S → Nothing happens</li>
<li>Manual save button doesn't work</li>
<li><code>onSave</code> callback never called</li>
</ul>
<p><strong>Causes:</strong></p>
<ol>
<li>Keyboard event listener not registered</li>
<li>forwardRef not working</li>
<li><code>save-page</code> command not registered</li>
</ol>
<p><strong>Solutions:</strong></p>
<ol>
<li>
<p><strong>Check keyboard listener:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-27-1"><a id="__codelineno-27-1" name="__codelineno-27-1" href="#__codelineno-27-1"></a><span class="c1">// In GrapesJSEditor useEffect</span>
</span><span id="__span-27-2"><a id="__codelineno-27-2" name="__codelineno-27-2" href="#__codelineno-27-2"></a><span class="kd">const</span><span class="w"> </span><span class="nx">handleKeyDown</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="o">:</span><span class="w"> </span><span class="kt">KeyboardEvent</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-27-3"><a id="__codelineno-27-3" name="__codelineno-27-3" href="#__codelineno-27-3"></a><span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Key pressed:&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;Ctrl:&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">ctrlKey</span><span class="p">);</span>
</span><span id="__span-27-4"><a id="__codelineno-27-4" name="__codelineno-27-4" href="#__codelineno-27-4"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">((</span><span class="nx">e</span><span class="p">.</span><span class="nx">ctrlKey</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">metaKey</span><span class="p">)</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">key</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s1">&#39;s&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-27-5"><a id="__codelineno-27-5" name="__codelineno-27-5" href="#__codelineno-27-5"></a><span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Save shortcut triggered&#39;</span><span class="p">);</span>
</span><span id="__span-27-6"><a id="__codelineno-27-6" name="__codelineno-27-6" href="#__codelineno-27-6"></a><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
</span><span id="__span-27-7"><a id="__codelineno-27-7" name="__codelineno-27-7" href="#__codelineno-27-7"></a><span class="w"> </span><span class="nx">editor</span><span class="p">.</span><span class="nx">runCommand</span><span class="p">(</span><span class="s1">&#39;save-page&#39;</span><span class="p">);</span>
</span><span id="__span-27-8"><a id="__codelineno-27-8" name="__codelineno-27-8" href="#__codelineno-27-8"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-27-9"><a id="__codelineno-27-9" name="__codelineno-27-9" href="#__codelineno-27-9"></a><span class="p">};</span>
</span></code></pre></div></p>
</li>
<li>
<p><strong>Verify ref handle:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-28-1"><a id="__codelineno-28-1" name="__codelineno-28-1" href="#__codelineno-28-1"></a><span class="c1">// In parent component</span>
</span><span id="__span-28-2"><a id="__codelineno-28-2" name="__codelineno-28-2" href="#__codelineno-28-2"></a><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Editor ref:&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">editorRef</span><span class="p">.</span><span class="nx">current</span><span class="p">);</span><span class="w"> </span><span class="c1">// Should be { triggerSave: fn }</span>
</span></code></pre></div></p>
</li>
<li>
<p><strong>Test command directly:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-29-1"><a id="__codelineno-29-1" name="__codelineno-29-1" href="#__codelineno-29-1"></a><span class="c1">// In browser console (after editor loads)</span>
</span><span id="__span-29-2"><a id="__codelineno-29-2" name="__codelineno-29-2" href="#__codelineno-29-2"></a><span class="nb">window</span><span class="p">.</span><span class="nx">editor</span><span class="p">.</span><span class="nx">runCommand</span><span class="p">(</span><span class="s1">&#39;save-page&#39;</span><span class="p">);</span>
</span><span id="__span-29-3"><a id="__codelineno-29-3" name="__codelineno-29-3" href="#__codelineno-29-3"></a><span class="c1">// Should trigger onSave callback</span>
</span></code></pre></div></p>
</li>
<li>
<p><strong>Check onSaveRef pattern:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-30-1"><a id="__codelineno-30-1" name="__codelineno-30-1" href="#__codelineno-30-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">onSaveRef</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">useRef</span><span class="p">(</span><span class="nx">onSave</span><span class="p">);</span>
</span><span id="__span-30-2"><a id="__codelineno-30-2" name="__codelineno-30-2" href="#__codelineno-30-2"></a><span class="nx">onSaveRef</span><span class="p">.</span><span class="nx">current</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">onSave</span><span class="p">;</span><span class="w"> </span><span class="c1">// Update on every render</span>
</span></code></pre></div></p>
</li>
</ol>
<hr />
<h3 id="problem-editor-crashes-on-large-pages">Problem: Editor Crashes on Large Pages<a class="headerlink" href="#problem-editor-crashes-on-large-pages" title="Permanent link">&para;</a></h3>
<p><strong>Symptoms:</strong></p>
<ul>
<li>Loading page with 100+ components → Tab freezes</li>
<li>GrapesJS UI unresponsive</li>
<li>Save takes 10+ seconds</li>
</ul>
<p><strong>Causes:</strong></p>
<ul>
<li>Too many components in single page</li>
<li>Deep nesting (10+ levels)</li>
<li>Heavy images without lazy loading</li>
</ul>
<p><strong>Solutions:</strong></p>
<ol>
<li><strong>Split into multiple pages:</strong></li>
<li>Separate hero, features, testimonials into 3 pages</li>
<li>
<p>Link pages via navigation</p>
</li>
<li>
<p><strong>Use CODE mode for complex layouts:</strong></p>
</li>
<li>Write HTML directly → Faster than GrapesJS rendering</li>
<li>
<p>Import via "Sync Overrides"</p>
</li>
<li>
<p><strong>Optimize images:</strong></p>
</li>
<li>Use external CDN (not base64-encoded)</li>
<li>Compress before upload</li>
<li>
<p>Lazy load below fold</p>
</li>
<li>
<p><strong>Increase browser memory:</strong></p>
</li>
<li>Chrome → <code>--max-old-space-size=4096</code></li>
<li>Edge → Similar flag</li>
</ol>
<hr />
<h3 id="problem-initial-data-not-loading">Problem: Initial Data Not Loading<a class="headerlink" href="#problem-initial-data-not-loading" title="Permanent link">&para;</a></h3>
<p><strong>Symptoms:</strong></p>
<ul>
<li>Editor opens with blank canvas</li>
<li><code>initialData</code> prop has data</li>
<li>Console shows no errors</li>
</ul>
<p><strong>Causes:</strong></p>
<ol>
<li><code>loadProjectData()</code> called before editor ready</li>
<li>Invalid JSON structure</li>
<li>Async timing issue</li>
</ol>
<p><strong>Solutions:</strong></p>
<ol>
<li>
<p><strong>Check editor ready state:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-31-1"><a id="__codelineno-31-1" name="__codelineno-31-1" href="#__codelineno-31-1"></a><span class="nx">useEffect</span><span class="p">(()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-31-2"><a id="__codelineno-31-2" name="__codelineno-31-2" href="#__codelineno-31-2"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nx">containerRef</span><span class="p">.</span><span class="nx">current</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="p">;</span>
</span><span id="__span-31-3"><a id="__codelineno-31-3" name="__codelineno-31-3" href="#__codelineno-31-3"></a>
</span><span id="__span-31-4"><a id="__codelineno-31-4" name="__codelineno-31-4" href="#__codelineno-31-4"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">editor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">grapesjs</span><span class="p">.</span><span class="nx">init</span><span class="p">({</span><span class="w"> </span><span class="cm">/* ... */</span><span class="w"> </span><span class="p">});</span>
</span><span id="__span-31-5"><a id="__codelineno-31-5" name="__codelineno-31-5" href="#__codelineno-31-5"></a>
</span><span id="__span-31-6"><a id="__codelineno-31-6" name="__codelineno-31-6" href="#__codelineno-31-6"></a><span class="w"> </span><span class="c1">// Wait for editor load event</span>
</span><span id="__span-31-7"><a id="__codelineno-31-7" name="__codelineno-31-7" href="#__codelineno-31-7"></a><span class="w"> </span><span class="nx">editor</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;load&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-31-8"><a id="__codelineno-31-8" name="__codelineno-31-8" href="#__codelineno-31-8"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">initialData</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">initialData</span><span class="p">).</span><span class="nx">length</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mf">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-31-9"><a id="__codelineno-31-9" name="__codelineno-31-9" href="#__codelineno-31-9"></a><span class="w"> </span><span class="nx">editor</span><span class="p">.</span><span class="nx">loadProjectData</span><span class="p">(</span><span class="nx">initialData</span><span class="p">);</span>
</span><span id="__span-31-10"><a id="__codelineno-31-10" name="__codelineno-31-10" href="#__codelineno-31-10"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-31-11"><a id="__codelineno-31-11" name="__codelineno-31-11" href="#__codelineno-31-11"></a><span class="w"> </span><span class="p">});</span>
</span><span id="__span-31-12"><a id="__codelineno-31-12" name="__codelineno-31-12" href="#__codelineno-31-12"></a><span class="p">},</span><span class="w"> </span><span class="p">[]);</span>
</span></code></pre></div></p>
</li>
<li>
<p><strong>Validate JSON:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-32-1"><a id="__codelineno-32-1" name="__codelineno-32-1" href="#__codelineno-32-1"></a><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Loading data:&#39;</span><span class="p">,</span><span class="w"> </span><span class="nb">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">initialData</span><span class="p">,</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> </span><span class="mf">2</span><span class="p">));</span>
</span><span id="__span-32-2"><a id="__codelineno-32-2" name="__codelineno-32-2" href="#__codelineno-32-2"></a><span class="c1">// Should have keys: assets, styles, pages</span>
</span></code></pre></div></p>
</li>
<li>
<p><strong>Handle empty data:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-33-1"><a id="__codelineno-33-1" name="__codelineno-33-1" href="#__codelineno-33-1"></a><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">initialData</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">initialData</span><span class="p">).</span><span class="nx">length</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mf">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-33-2"><a id="__codelineno-33-2" name="__codelineno-33-2" href="#__codelineno-33-2"></a><span class="w"> </span><span class="nx">editor</span><span class="p">.</span><span class="nx">loadProjectData</span><span class="p">(</span><span class="nx">initialData</span><span class="p">);</span>
</span><span id="__span-33-3"><a id="__codelineno-33-3" name="__codelineno-33-3" href="#__codelineno-33-3"></a><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-33-4"><a id="__codelineno-33-4" name="__codelineno-33-4" href="#__codelineno-33-4"></a><span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Starting with blank canvas&#39;</span><span class="p">);</span>
</span><span id="__span-33-5"><a id="__codelineno-33-5" name="__codelineno-33-5" href="#__codelineno-33-5"></a><span class="p">}</span>
</span></code></pre></div></p>
</li>
</ol>
<hr />
<h3 id="problem-styles-not-applying-in-canvas">Problem: Styles Not Applying in Canvas<a class="headerlink" href="#problem-styles-not-applying-in-canvas" title="Permanent link">&para;</a></h3>
<p><strong>Symptoms:</strong></p>
<ul>
<li>Drag block to canvas → No background color</li>
<li>Text has wrong font</li>
<li>Layout broken</li>
</ul>
<p><strong>Causes:</strong></p>
<ol>
<li>Inline styles not supported</li>
<li>External stylesheet missing</li>
<li>Canvas iframe CSP issue</li>
</ol>
<p><strong>Solutions:</strong></p>
<ol>
<li>
<p><strong>Use inline styles in generateBlockHtml():</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-34-1"><a id="__codelineno-34-1" name="__codelineno-34-1" href="#__codelineno-34-1"></a><span class="c1">// Good</span>
</span><span id="__span-34-2"><a id="__codelineno-34-2" name="__codelineno-34-2" href="#__codelineno-34-2"></a><span class="k">return</span><span class="w"> </span><span class="sb">`&lt;section style=&quot;padding: 40px; background: #f00;&quot;&gt;...&lt;/section&gt;`</span><span class="p">;</span>
</span><span id="__span-34-3"><a id="__codelineno-34-3" name="__codelineno-34-3" href="#__codelineno-34-3"></a>
</span><span id="__span-34-4"><a id="__codelineno-34-4" name="__codelineno-34-4" href="#__codelineno-34-4"></a><span class="c1">// Bad (requires CSS injection)</span>
</span><span id="__span-34-5"><a id="__codelineno-34-5" name="__codelineno-34-5" href="#__codelineno-34-5"></a><span class="k">return</span><span class="w"> </span><span class="sb">`&lt;section class=&quot;hero&quot;&gt;...&lt;/section&gt;`</span><span class="p">;</span>
</span></code></pre></div></p>
</li>
<li>
<p><strong>Inject fonts into canvas:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-35-1"><a id="__codelineno-35-1" name="__codelineno-35-1" href="#__codelineno-35-1"></a><span class="nx">canvas</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-35-2"><a id="__codelineno-35-2" name="__codelineno-35-2" href="#__codelineno-35-2"></a><span class="w"> </span><span class="nx">styles</span><span class="o">:</span><span class="w"> </span><span class="p">[</span>
</span><span id="__span-35-3"><a id="__codelineno-35-3" name="__codelineno-35-3" href="#__codelineno-35-3"></a><span class="w"> </span><span class="s1">&#39;https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;display=swap&#39;</span><span class="p">,</span>
</span><span id="__span-35-4"><a id="__codelineno-35-4" name="__codelineno-35-4" href="#__codelineno-35-4"></a><span class="w"> </span><span class="p">],</span>
</span><span id="__span-35-5"><a id="__codelineno-35-5" name="__codelineno-35-5" href="#__codelineno-35-5"></a><span class="p">}</span>
</span></code></pre></div></p>
</li>
<li>
<p><strong>Check iframe sandbox:</strong>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-36-1"><a id="__codelineno-36-1" name="__codelineno-36-1" href="#__codelineno-36-1"></a><span class="c1">// GrapesJS canvas uses &lt;iframe&gt; — ensure no sandbox restrictions</span>
</span><span id="__span-36-2"><a id="__codelineno-36-2" name="__codelineno-36-2" href="#__codelineno-36-2"></a><span class="c1">// Default config works, but custom CSP may block</span>
</span></code></pre></div></p>
</li>
</ol>
<hr />
<h2 id="performance-optimization">Performance Optimization<a class="headerlink" href="#performance-optimization" title="Permanent link">&para;</a></h2>
<h3 id="lazy-loading">Lazy Loading<a class="headerlink" href="#lazy-loading" title="Permanent link">&para;</a></h3>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-37-1"><a id="__codelineno-37-1" name="__codelineno-37-1" href="#__codelineno-37-1"></a><span class="c1">// In LandingPageEditor.tsx</span>
</span><span id="__span-37-2"><a id="__codelineno-37-2" name="__codelineno-37-2" href="#__codelineno-37-2"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">lazy</span><span class="p">,</span><span class="w"> </span><span class="nx">Suspense</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span><span id="__span-37-3"><a id="__codelineno-37-3" name="__codelineno-37-3" href="#__codelineno-37-3"></a>
</span><span id="__span-37-4"><a id="__codelineno-37-4" name="__codelineno-37-4" href="#__codelineno-37-4"></a><span class="kd">const</span><span class="w"> </span><span class="nx">GrapesJSEditor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">lazy</span><span class="p">(()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="k">import</span><span class="p">(</span><span class="s1">&#39;@/components/GrapesJSEditor&#39;</span><span class="p">));</span>
</span><span id="__span-37-5"><a id="__codelineno-37-5" name="__codelineno-37-5" href="#__codelineno-37-5"></a>
</span><span id="__span-37-6"><a id="__codelineno-37-6" name="__codelineno-37-6" href="#__codelineno-37-6"></a><span class="k">return</span><span class="w"> </span><span class="p">(</span>
</span><span id="__span-37-7"><a id="__codelineno-37-7" name="__codelineno-37-7" href="#__codelineno-37-7"></a><span class="w"> </span><span class="o">&lt;</span><span class="nx">Suspense</span><span class="w"> </span><span class="nx">fallback</span><span class="o">=</span><span class="p">{</span><span class="o">&lt;</span><span class="nx">Spin</span><span class="w"> </span><span class="nx">size</span><span class="o">=</span><span class="s2">&quot;large&quot;</span><span class="w"> </span><span class="o">/&gt;</span><span class="p">}</span><span class="o">&gt;</span>
</span><span id="__span-37-8"><a id="__codelineno-37-8" name="__codelineno-37-8" href="#__codelineno-37-8"></a><span class="w"> </span><span class="o">&lt;</span><span class="nx">GrapesJSEditor</span><span class="w"> </span><span class="nx">ref</span><span class="o">=</span><span class="p">{</span><span class="nx">editorRef</span><span class="p">}</span><span class="w"> </span><span class="nx">onSave</span><span class="o">=</span><span class="p">{</span><span class="nx">handleSave</span><span class="p">}</span><span class="w"> </span><span class="o">/&gt;</span>
</span><span id="__span-37-9"><a id="__codelineno-37-9" name="__codelineno-37-9" href="#__codelineno-37-9"></a><span class="w"> </span><span class="o">&lt;</span><span class="err">/Suspense&gt;</span>
</span><span id="__span-37-10"><a id="__codelineno-37-10" name="__codelineno-37-10" href="#__codelineno-37-10"></a><span class="p">);</span>
</span></code></pre></div>
<p><strong>Benefit:</strong> Reduces initial bundle size by ~800KB (GrapesJS + plugins)</p>
<h3 id="debounced-auto-save">Debounced Auto-Save<a class="headerlink" href="#debounced-auto-save" title="Permanent link">&para;</a></h3>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-38-1"><a id="__codelineno-38-1" name="__codelineno-38-1" href="#__codelineno-38-1"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">useRef</span><span class="p">,</span><span class="w"> </span><span class="nx">useEffect</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span><span id="__span-38-2"><a id="__codelineno-38-2" name="__codelineno-38-2" href="#__codelineno-38-2"></a>
</span><span id="__span-38-3"><a id="__codelineno-38-3" name="__codelineno-38-3" href="#__codelineno-38-3"></a><span class="kd">const</span><span class="w"> </span><span class="nx">autoSaveTimerRef</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">useRef</span><span class="o">&lt;</span><span class="nx">ReturnType</span><span class="o">&lt;</span><span class="ow">typeof</span><span class="w"> </span><span class="nx">setTimeout</span><span class="o">&gt;&gt;</span><span class="p">();</span>
</span><span id="__span-38-4"><a id="__codelineno-38-4" name="__codelineno-38-4" href="#__codelineno-38-4"></a>
</span><span id="__span-38-5"><a id="__codelineno-38-5" name="__codelineno-38-5" href="#__codelineno-38-5"></a><span class="kd">const</span><span class="w"> </span><span class="nx">handleEditorChange</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-38-6"><a id="__codelineno-38-6" name="__codelineno-38-6" href="#__codelineno-38-6"></a><span class="w"> </span><span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">autoSaveTimerRef</span><span class="p">.</span><span class="nx">current</span><span class="p">);</span>
</span><span id="__span-38-7"><a id="__codelineno-38-7" name="__codelineno-38-7" href="#__codelineno-38-7"></a><span class="w"> </span><span class="nx">autoSaveTimerRef</span><span class="p">.</span><span class="nx">current</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-38-8"><a id="__codelineno-38-8" name="__codelineno-38-8" href="#__codelineno-38-8"></a><span class="w"> </span><span class="nx">editorRef</span><span class="p">.</span><span class="nx">current</span><span class="o">?</span><span class="p">.</span><span class="nx">triggerSave</span><span class="p">();</span>
</span><span id="__span-38-9"><a id="__codelineno-38-9" name="__codelineno-38-9" href="#__codelineno-38-9"></a><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="mf">5000</span><span class="p">);</span><span class="w"> </span><span class="c1">// Auto-save after 5s of inactivity</span>
</span><span id="__span-38-10"><a id="__codelineno-38-10" name="__codelineno-38-10" href="#__codelineno-38-10"></a><span class="p">};</span>
</span><span id="__span-38-11"><a id="__codelineno-38-11" name="__codelineno-38-11" href="#__codelineno-38-11"></a>
</span><span id="__span-38-12"><a id="__codelineno-38-12" name="__codelineno-38-12" href="#__codelineno-38-12"></a><span class="nx">useEffect</span><span class="p">(()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-38-13"><a id="__codelineno-38-13" name="__codelineno-38-13" href="#__codelineno-38-13"></a><span class="w"> </span><span class="c1">// Listen to editor change events</span>
</span><span id="__span-38-14"><a id="__codelineno-38-14" name="__codelineno-38-14" href="#__codelineno-38-14"></a><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">editor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">window</span><span class="p">.</span><span class="nx">editor</span><span class="p">;</span><span class="w"> </span><span class="c1">// Access via global (not recommended for prod)</span>
</span><span id="__span-38-15"><a id="__codelineno-38-15" name="__codelineno-38-15" href="#__codelineno-38-15"></a><span class="w"> </span><span class="nx">editor</span><span class="o">?</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;component:update&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">handleEditorChange</span><span class="p">);</span>
</span><span id="__span-38-16"><a id="__codelineno-38-16" name="__codelineno-38-16" href="#__codelineno-38-16"></a><span class="w"> </span><span class="nx">editor</span><span class="o">?</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;style:update&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">handleEditorChange</span><span class="p">);</span>
</span><span id="__span-38-17"><a id="__codelineno-38-17" name="__codelineno-38-17" href="#__codelineno-38-17"></a>
</span><span id="__span-38-18"><a id="__codelineno-38-18" name="__codelineno-38-18" href="#__codelineno-38-18"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-38-19"><a id="__codelineno-38-19" name="__codelineno-38-19" href="#__codelineno-38-19"></a><span class="w"> </span><span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">autoSaveTimerRef</span><span class="p">.</span><span class="nx">current</span><span class="p">);</span>
</span><span id="__span-38-20"><a id="__codelineno-38-20" name="__codelineno-38-20" href="#__codelineno-38-20"></a><span class="w"> </span><span class="nx">editor</span><span class="o">?</span><span class="p">.</span><span class="nx">off</span><span class="p">(</span><span class="s1">&#39;component:update&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">handleEditorChange</span><span class="p">);</span>
</span><span id="__span-38-21"><a id="__codelineno-38-21" name="__codelineno-38-21" href="#__codelineno-38-21"></a><span class="w"> </span><span class="nx">editor</span><span class="o">?</span><span class="p">.</span><span class="nx">off</span><span class="p">(</span><span class="s1">&#39;style:update&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">handleEditorChange</span><span class="p">);</span>
</span><span id="__span-38-22"><a id="__codelineno-38-22" name="__codelineno-38-22" href="#__codelineno-38-22"></a><span class="w"> </span><span class="p">};</span>
</span><span id="__span-38-23"><a id="__codelineno-38-23" name="__codelineno-38-23" href="#__codelineno-38-23"></a><span class="p">},</span><span class="w"> </span><span class="p">[]);</span>
</span></code></pre></div>
<p><strong>Trade-off:</strong> More API calls vs. reduced data loss risk</p>
<hr />
<h2 id="related-documentation">Related Documentation<a class="headerlink" href="#related-documentation" title="Permanent link">&para;</a></h2>
<h3 id="components">Components<a class="headerlink" href="#components" title="Permanent link">&para;</a></h3>
<ul>
<li><strong><a href="/v2/frontend/pages/LandingPageEditor">LandingPageEditor</a></strong> — Full-screen editor wrapper</li>
<li><strong><a href="/v2/frontend/pages/LandingPagesPage">LandingPagesPage</a></strong> — Table view with edit links</li>
</ul>
<h3 id="features">Features<a class="headerlink" href="#features" title="Permanent link">&para;</a></h3>
<ul>
<li><strong><a href="../page-builder/">Page Builder</a></strong> — Complete page builder system</li>
<li><strong><a href="../block-library/">Block Library</a></strong> — Custom blocks database</li>
<li><strong><a href="../mkdocs-export/">MkDocs Export</a></strong> — Export to documentation site</li>
</ul>
<h3 id="external">External<a class="headerlink" href="#external" title="Permanent link">&para;</a></h3>
<ul>
<li><strong><a href="https://grapesjs.com/docs/">GrapesJS Docs</a></strong> — Official documentation</li>
<li><strong><a href="https://grapesjs.com/docs/api/">GrapesJS API</a></strong> — JavaScript API reference</li>
<li><strong><a href="https://grapesjs.com/docs/plugins/">GrapesJS Plugins</a></strong> — Plugin ecosystem</li>
</ul>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
Back to top
</button>
</main>
<footer class="md-footer">
<nav class="md-footer__inner md-grid" aria-label="Footer" >
<a href="../page-builder/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Page Builder">
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</div>
<div class="md-footer__title">
<span class="md-footer__direction">
Previous
</span>
<div class="md-ellipsis">
Page Builder
</div>
</div>
</a>
<a href="../block-library/" class="md-footer__link md-footer__link--next" aria-label="Next: Block Library">
<div class="md-footer__title">
<span class="md-footer__direction">
Next
</span>
<div class="md-ellipsis">
Block Library
</div>
</div>
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11z"/></svg>
</div>
</a>
</nav>
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
<div class="md-copyright__highlight">
Copyright &copy; 2024 The Bunker Operations <a href="#__consent">Change cookie settings</a>
</div>
</div>
<div class="md-social">
<a href="https://gitea.bnkops.com/admin" target="_blank" rel="noopener" title="Gitea Repository" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
</a>
<a href="https://listmonk.bnkops.com/subscription/form" target="_blank" rel="noopener" title="Newsletter" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M536.4-26.3c9.8-3.5 20.6-1 28 6.3s9.8 18.2 6.3 28l-178 496.9c-5 13.9-18.1 23.1-32.8 23.1-14.2 0-27-8.6-32.3-21.7l-64.2-158c-4.5-11-2.5-23.6 5.2-32.6l94.5-112.4c5.1-6.1 4.7-15-.9-20.6s-14.6-6-20.6-.9l-112.4 94.3c-9.1 7.6-21.6 9.6-32.6 5.2L38.1 216.8c-13.1-5.3-21.7-18.1-21.7-32.3 0-14.7 9.2-27.8 23.1-32.8z"/></svg>
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"annotate": null, "base": "../../../..", "features": ["announce.dismiss", "content.action.edit", "content.action.view", "content.code.annotate", "content.code.copy", "content.tooltips", "navigation.expand", "navigation.footer", "navigation.indexes", "navigation.path", "navigation.prune", "navigation.sections", "navigation.tabs", "navigation.tabs.sticky", "navigation.top", "navigation.tracking", "search.highlight", "search.share", "search.suggest", "toc.follow"], "search": "../../../../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
<script src="../../../../assets/javascripts/bundle.79ae519e.min.js"></script>
<script src="../../../../javascripts/home.js"></script>
<script src="../../../../javascripts/github-widget.js"></script>
<script src="../../../../javascripts/gitea-widget.js"></script>
<script src="../../../../assets/js/env-config.js"></script>
<script src="../../../../assets/js/video-player.js"></script>
</body>
</html>