| name | elementor-to-generateblocks |
| version | 1.0.0 |
| description | Convert Elementor layouts to clean GenerateBlocks V2 format, eliminating DIVception |
| author | Gaurav Tiwari |
| updated | "2026-01-22T00:00:00.000Z" |
| trigger | ["Elementor to GenerateBlocks","convert Elementor","Elementor migration","clean up Elementor","simplify Elementor"] |
| tags | ["wordpress","generateblocks","elementor","conversion","migration"] |
Elementor to GenerateBlocks Converter
Convert bloated Elementor layouts to clean, semantic GenerateBlocks V2 blocks.
Read first
Before converting, read these files in the sibling generateblocks-layouts skill:
../generateblocks-layouts/references/_index.md — task router
../generateblocks-layouts/references/recovery-rules.md — every cause of
"Attempt Recovery" errors with the exact fix
../generateblocks-layouts/references/block-types.md — attribute specs
For Elementor Posts/Loop widgets, build the GB equivalent with the V2 Query
block family — see ../generateblocks-layouts/references/query-block.md.
For Elementor widgets that map to Pro features (accordion, tabs, carousel,
sticky header), see ../generateblocks-layouts/references/gb-pro.md.
Output Requirements
ALWAYS output converted blocks to a file, never inline in the chat.
- Output filename:
{section-name}-converted.html (e.g., hero-converted.html)
- For full page conversions: Split into multiple files by section
- Include a brief summary in chat describing what was converted
Why file output?
- Converted block code is often 100+ lines
- Easier to copy/paste into WordPress
- Prevents truncation and formatting issues
- Allows comparison with original Elementor output
The DIVception Problem
Elementor wraps everything in excessive nested divs with utility classes:
<section class="elementor-section elementor-top-section elementor-element elementor-element-abc123 elementor-section-boxed elementor-section-height-default elementor-section-height-default">
<div class="elementor-container elementor-column-gap-default">
<div class="elementor-column elementor-col-50 elementor-top-column elementor-element elementor-element-def456">
<div class="elementor-widget-wrap elementor-element-populated">
<div class="elementor-element elementor-element-ghi789 elementor-widget elementor-widget-heading">
<div class="elementor-widget-container">
<h2 class="elementor-heading-title elementor-size-default">Hello World</h2>
</div>
</div>
</div>
</div>
</div>
</section>
Result: 7 nested divs for a simple heading.
GenerateBlocks Solution
Same content, cleaner structure:
<section class="gb-element-sect001 gb-element">
<div class="gb-element-sect001a gb-element">
<h2 class="gb-text gb-text-sect001b">Hello World</h2>
</div>
</section>
Result: 3 semantic elements. Clean, maintainable, fast.
Conversion Rules
1. Flatten the Structure
| Elementor Pattern | GenerateBlocks Equivalent |
|---|
elementor-section > elementor-container | Single section + inner div |
elementor-column > elementor-widget-wrap | Single div with flex/grid |
elementor-widget > elementor-widget-container | Direct content block |
| Multiple wrapper divs | Remove all unnecessary wrappers |
2. Map Elementor Classes to Inline Styles
| Elementor Class | CSS Property |
|---|
elementor-section-boxed | max-width: var(--gb-container-width); margin: auto |
elementor-section-full_width | max-width: 100% |
elementor-column-gap-default | gap: 1rem |
elementor-column-gap-extended | gap: 2rem |
elementor-col-50 | flex: 0 0 50% or grid column |
elementor-col-33 | flex: 0 0 33.333% or grid column |
elementor-hidden-desktop | @media(min-width:1025px){display:none} |
elementor-hidden-tablet | @media(max-width:1024px){display:none} |
elementor-hidden-mobile | @media(max-width:767px){display:none} |
3. Widget to Block Mapping
| Elementor Widget | GenerateBlocks Block |
|---|
elementor-widget-heading | generateblocks/text with h1-h6 |
elementor-widget-text-editor | generateblocks/text with p/div |
elementor-widget-button (text only) | generateblocks/text with tagName="a" (no htmlAttributes for href) |
elementor-widget-button (with icon) | generateblocks/element with tagName="a" wrapping text + shape blocks |
elementor-widget-image | generateblocks/media (or core/image if caption needed) |
elementor-widget-icon | generateblocks/shape with inline SVG |
elementor-widget-icon-box | generateblocks/element container with shape + text blocks |
elementor-widget-image-box | generateblocks/element container |
elementor-widget-spacer | Remove (use margins/padding instead) |
elementor-widget-divider | generateblocks/element with border (or core/separator) |
| Clickable card (with inner blocks) | generateblocks/element with tagName="a" + htmlAttributes: {"href":"https://example.com/"} |
4. Widgets Requiring Core Blocks
Some Elementor widgets should convert to Core Blocks instead of GenerateBlocks:
| Elementor Widget | Use Core Block | Reason |
|---|
elementor-widget-video | core/video or core/embed | Native player, embed support |
elementor-widget-image-gallery | core/gallery | Lightbox, columns, captions |
elementor-widget-image-carousel | core/gallery + CSS or third-party | Slider functionality |
elementor-widget-audio | core/audio | Native audio player |
elementor-widget-table | core/table | Semantic table structure |
elementor-widget-blockquote | core/quote | Semantic quote with citation |
elementor-widget-code-highlight | core/code | Preformatted code display |
elementor-widget-text-path | Keep as SVG | No Core equivalent |
elementor-widget-lottie | Keep as custom | No Core equivalent |
elementor-widget-image (with caption) | core/image | Built-in caption support |
elementor-widget-tabs | core/details or custom | Accordion/tabs functionality |
elementor-widget-accordion | core/details | Native disclosure widget |
elementor-widget-toggle | core/details | Native disclosure widget |
| Text with emojis | core/paragraph | GenerateBlocks doesn't render emojis properly |
Rule: Use GenerateBlocks for layout and styling. Use Core Blocks for specialized functionality (media players, embeds, tables, interactive elements).
4. Column Layouts
Elementor 2-column:
<div class="elementor-container">
<div class="elementor-column elementor-col-50">...</div>
<div class="elementor-column elementor-col-50">...</div>
</div>
GenerateBlocks equivalent:
<div class="gb-element-col001 gb-element">
</div>
5. Common Elementor Patterns
Hero with background:
<section class="gb-element-hero001 gb-element">
</section>
Card grid:
<div class="gb-element-cards001 gb-element">
</div>
Design Inference (When CSS Not Provided)
When converting Elementor without CSS values, infer styles based on context:
Default GeneratePress Design System
Use these defaults when no specific styles are provided:
Colors:
- Primary:
#0073e6 (GeneratePress default accent)
- Text:
#222222
- Muted text:
#757575
- Background:
#ffffff
- Light background:
#f7f8f9
- Border:
#e0e0e0
Typography:
- Body:
17px, line-height 1.7
- H1:
42px, font-weight 600
- H2:
35px, font-weight 600
- H3:
29px, font-weight 600
- H4:
24px, font-weight 600
Spacing:
- Section padding:
60px top/bottom
- Container max-width:
var(--gb-container-width)
- Content padding:
20px
- Gap between elements:
20px
Buttons:
- Padding:
15px 30px
- Border-radius:
4px
- Background: primary color
- Hover: darken 10%
Site-Specific Inference
When the target site is known, extract design tokens from:
- Theme's style.css - Primary colors, fonts, base sizes
- theme.json (block themes) - Color palette, typography presets
- Existing pages - Match the established visual language
Example inference for gauravtiwari.org:
{
"colors": {
"primary": "#c0392b",
"text": "#0a0a0a",
"muted": "#5c5c5c",
"background": "#ffffff",
"lightBg": "#f5f5f3",
"border": "#e5e5e5"
},
"typography": {
"body": "1rem",
"h1": "clamp(2rem, 5vw, 3.5rem)",
"h2": "clamp(1.5rem, 3vw, 2.5rem)",
"fontWeight": "900 for headings"
},
"spacing": {
"sectionPadding": "4rem",
"containerMax": "var(--gb-container-width)",
"gap": "1rem to 2rem"
},
"effects": {
"borderRadius": "1rem for cards, 2rem for buttons",
"hoverLift": "translateY(-6px)",
"shadow": "0 20px 60px rgba(0,0,0,0.15)"
}
}
CRITICAL: No Extra HTML Comments
⛔ NEVER add HTML comments other than WordPress block markers.
The ONLY allowed comments are WordPress block delimiters:
<!-- wp:generateblocks/element {...} --> and <!-- /wp:generateblocks/element -->
<!-- wp:generateblocks/text {...} --> and <!-- /wp:generateblocks/text -->
<!-- wp:generateblocks/media {...} --> and <!-- /wp:generateblocks/media -->
<!-- wp:generateblocks/shape {...} --> and <!-- /wp:generateblocks/shape -->
<!-- wp:image {...} --> and <!-- /wp:image -->
<!-- wp:video {...} --> and <!-- /wp:video -->
<!-- wp:gallery {...} --> and <!-- /wp:gallery -->
- Any other
<!-- wp:{namespace}/{block} --> format
WRONG - These will break the block editor:
CORRECT - Only block delimiters:
<section class="gb-element-sect001 gb-element">
<h2 class="gb-text-sect001a gb-text">Hello World</h2>
</section>
Any extra HTML comments will break the WordPress block editor and cause parsing errors. This is non-negotiable.
Critical Rules
1. htmlAttributes MUST Use Object Format
htmlAttributes MUST be a plain object, NOT an array of objects:
"htmlAttributes": {"href": "https://example.com/contact/", "target": "_blank", "id": "section-id"}
"htmlAttributes": [
{"attribute": "href", "value": "/contact/"},
{"attribute": "target", "value": "_blank"},
{"attribute": "id", "value": "section-id"}
]
Note: linkHtmlAttributes for media blocks may use a different format.
2. Always Include Both styles and css
Every block needs:
styles object with camelCase properties (supports responsive keys like "@media (max-width:1024px)":{...})
css string with minified CSS (kebab-case, alphabetically sorted, base styles only)
- The
css attribute must NOT contain hover states or transitions (the plugin generates those from the styles object)
- Exceptions that go in
css: pseudo-elements (::before/::after), media queries, animations, parent hover targeting children
3. Element Blocks Need className
Add "className":"gb-element-{id} gb-element" to all element block attributes (className MUST include the uniqueId). HTML class order: gb-element-{id} gb-element:
<div class="gb-element-card001 gb-element">...</div>
4. Text <a> vs Element <a> Links
| Block Type | htmlAttributes for href | Use Case |
|---|
generateblocks/text with tagName: "a" | No - plugin manages link internally | Plain text buttons/links (no inner blocks) |
generateblocks/element with tagName: "a" | Yes - {"href":"https://example.com/"} | Containers wrapping inner blocks (cards, icon buttons) |
5. SVG Icons Use Shape Blocks
Convert Elementor icons to generateblocks/shape (not generateblocks/element with raw SVG):
<span class="gb-shape gb-shape-icon001"><svg viewBox="0 0 24 24" fill="currentColor"><path d="..."/></svg></span>
6. Lists Use core/list with .list Class
Convert Elementor list widgets to native WordPress list block:
<ul class="wp-block-list list">...</ul>
7. Container Width with CSS Variable
Use --gb-container-width for inner container width and align: "full" on parent section:
{"align": "full", "styles": {"maxWidth": "var(\u002d\u002dgb-container-width)"}}
8. Use Semantic HTML
Replace Elementor's generic divs with proper tags:
| Content Type | Tag |
|---|
| Page section | section |
| Header area | header |
| Footer area | footer |
| Navigation | nav |
| Main content | article |
| Sidebar | aside |
| Card wrapper | div or a (if clickable) |
9. Remove Spacers
Never convert elementor-widget-spacer to a block. Use:
marginBottom on preceding element
marginTop on following element
gap on parent container
10. Flatten Icon Boxes
Elementor icon box (simplified):
<div class="elementor-icon-box">
<div class="elementor-icon-box-icon">
<span class="elementor-icon"><i class="fab fa-wordpress"></i></span>
</div>
<div class="elementor-icon-box-content">
<h3>Title</h3>
<p>Description</p>
</div>
</div>
GenerateBlocks:
<div class="gb-element-ibox001 gb-element">
<span class="gb-shape gb-shape-ibox001a"><svg viewBox="0 0 24 24" fill="currentColor"><path d="..."/></svg></span>
<div class="gb-element-ibox001b gb-element">
<h3 class="gb-text gb-text-ibox001c">Title</h3>
<p class="gb-text gb-text-ibox001d">Description</p>
</div>
</div>
11. Full Absolute URLs
Always use full absolute URLs in htmlAttributes, never relative paths:
"htmlAttributes": {"href": "https://example.com/contact/"}
"htmlAttributes": {"href": "/contact/"}
12. No Spaces in CSS Functions
The block editor minifies CSS, so spaces in functions like clamp() cause mismatches:
font-size:clamp(2rem,5vw,3rem)
font-size:clamp(2rem, 5vw, 3rem)
13. SVG Attribute Order in HTML
SVG elements must follow this attribute order: viewBox first, then fill, then aria-hidden:
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="..."/></svg>
<svg fill="currentColor" viewBox="0 0 24 24"><path d="..."/></svg>
14. Compact Nesting
Closing tags must be on the same line as their parent's closing comment. No blank lines between closing tags:
</div>
</section>
</div>
</section>
Conversion Workflow
- Analyze Elementor HTML - Identify sections, columns, widgets
- Extract content - Isolate actual text, images, links
- Determine layout - Map column structure to grid/flex
- Apply design tokens - Use provided CSS or infer from site
- Build blocks - Create GenerateBlocks with inline styles
- Test responsive - Add media queries for breakpoints
- Validate - Ensure no HTML comments except block markers
Performance Benefits
| Metric | Elementor | GenerateBlocks |
|---|
| DOM nodes | 50-100+ per section | 5-15 per section |
| CSS file size | 200KB+ | 0 (inline) |
| Render blocking | Multiple CSS files | None |
| First paint | Delayed | Fast |
| CLS issues | Common | Rare |
Font Awesome to SVG
Replace Elementor's Font Awesome icons with inline SVGs:
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
</svg>
See SVG Icons Reference for common icons.