Elementor solved a real problem.
WordPress needed a visual editor. People wanted to drag and drop, see changes in real time, customize anything without touching code. Elementor delivered. The idea was great — similar to Webflow, it gave everyone power settings to control anything they wanted.
But it grew into something bigger than it should have been.
A CMS inside a CMS
What started as a landing page builder became its own content management system — sitting inside WordPress.
Elementor has its own template system. WordPress already has a template system. Now you have two.
Elementor has its own global colors. WordPress has theme.json color palettes. Now you have two.
Elementor has its own typography settings. WordPress has theme.json typography. Now you have two.
Elementor has its own responsive breakpoints. Its own popup system. Its own form builder. Its own dynamic tags. Its own conditions logic. Its own theme builder. Its own WooCommerce integration.
Every feature WordPress adds, Elementor already built their own version years ago. Now you’re managing two of everything, and they don’t always agree.
I’ve opened sites where the theme says one font, Elementor says another, and the customizer says a third. Which one wins? Depends on the page. Depends on the element. Depends on whether Mercury is in retrograde.
What’s actually running under the hood
Install Elementor and your database starts filling up. A custom post type called elementor_library stores every template, popup, and saved section. Your page content gets saved as serialized data in wp_postmeta. Every page generates its own CSS file in /wp-content/uploads/elementor/css/.
Your 5-page brochure site is carrying the weight of a full application framework.
The dependency chain
Here’s what nobody tells you when you start with Elementor: you’re not building a WordPress site anymore. You’re building an Elementor site that happens to run on WordPress.
Your content is wrapped in Elementor’s data structure. Your layout depends on Elementor’s grid. Your styles depend on Elementor’s generated CSS. Your templates depend on Elementor’s conditions system.
Deactivate the plugin and your site doesn’t just look different. Layouts break. Sections disappear. Styling vanishes. What’s left is unstyled HTML — divs with class names that mean nothing without Elementor’s CSS to interpret them.
That’s not a WordPress site. That’s a dependency.
That power comes at a cost.
The CSS nightmare
Elementor gives you control over everything — font sizes, padding, margin, colors, hover states. Sounds great until you need to override something.
Those values are hardcoded per element, per breakpoint. Inline styles or hyper-specific selectors, with !important scattered throughout.
I once spent way too long trying to change a button’s hover color. The client wanted blue instead of green. Should have been one line of CSS. But Elementor’s styles had higher specificity. My CSS didn’t work. Their styles won. So I wrote a more specific selector. Still didn’t work — they had !important. So I added !important. Still fighting.
Their own documentation tells you to use !important when overriding button styles with custom CSS. That’s not a workaround. That’s the official method.
Client wants the body font 2px bigger? That’s not a global setting. That’s hunting through widgets. Client wants more whitespace throughout the site? That’s not a spacing scale update. That’s opening every section, every column, every element.
Visual freedom becomes technical debt.
Visual freedom becomes technical debt.
With Gutenberg, you can change colors right from the admin Full Site Editing interface. It’s global. You don’t even need to update your theme.json — just change it in the editor and it applies everywhere, instantly.
Fighting the nature of HTML
The industry moved away from aggressive CSS resets years ago. Browser defaults exist for a reason. A <ul> should look like a list. A <blockquote> should look like a quote. Semantic HTML has built-in behavior — modern development works with it, not against it.
Elementor resets aggressively, then restyles everything their way. A <ul> inside Elementor often doesn’t behave like a <ul>. List markers stripped. Padding gone. Default spacing removed.
I’ve written custom CSS just to make a list look like a list again:
.elementor-widget-text-editor ul {
list-style: disc;
padding-left: 1.5em;
margin-bottom: 1em;
}
.elementor-widget-text-editor ul li {
margin-bottom: 0.5em;
}
That’s not styling. That’s restoration.
Same story with tables, blockquotes, nested lists. Every semantic HTML element needs its default behavior rebuilt.
Every Elementor update is a gamble.
Update and something breaks — a widget looks different, a layout shifts, a feature changes. But you can’t skip updates because security vulnerabilities are real.
So you clone to staging. You update. You check every page. You push to production and wait for the client email.
Meanwhile, WordPress updates. Your theme updates. PHP updates. And Elementor has to keep up with all of it. Three moving targets, one plugin trying to stay compatible with everything.
Gutenberg went the other way
Minimal styling out of the box. Just enough to not look broken. No inline styles forced on you. No specificity wars.
A list is a list. A heading is a heading. A blockquote looks like a blockquote. You’re styling semantic HTML, not fighting it.
theme.json is the single source of truth. Colors, spacing, typography — defined once, applied everywhere. CSS custom properties you can actually use.
The first time I set up a spacing scale in theme.json and watched it apply across the entire site, I almost cried. One file. One source of truth. Change a value, see it everywhere.
{
"settings": {
"spacing": {
"spacingSizes": [
{ "size": "0.5rem", "slug": "20", "name": "Small" },
{ "size": "1rem", "slug": "40", "name": "Medium" },
{ "size": "2rem", "slug": "60", "name": "Large" }
]
}
}
}
Client wants more whitespace? I change one value. Done. Not hunting through widgets. One value.
Client wants a rebrand? Update theme.json. New colors cascade everywhere they’re supposed to.
Accessibility audit requires larger font sizes? Global update. Ten seconds.
New developer joins the project? They read theme.json, they understand the system. No archaeology required.
Elementor looks easier. Drag and drop, visual editing, what-you-see-is-what-you-get.
The client experience
But I’ve watched clients freeze in front of Elementor’s interface. Too many options. Too many panels. They’re terrified of breaking something. So they call me to change a phone number.
Gutenberg with well-built blocks is actually simpler. Clients see their content, not a control panel. They click, they type, they save. They don’t need to understand columns and sections and widgets. They just edit their words.
I have clients updating their own sites now. Weekly blog posts. Event announcements. Team member changes. They don’t call me for content updates anymore.
That’s not lost revenue. That’s the whole point.
Everyone bashes Gutenberg. Too basic. Not enough control. Can’t do what Elementor does.
But look at Elementor 4. They’re copying Gutenberg’s block-based approach. They see where things are going.
The problem is they’re trapped. An ecosystem built on top of another ecosystem. All that legacy code, all those inline styles, all those hyper-specific selectors — they can’t just delete it. Millions of sites depend on that code behaving exactly as it does.
They’re stuck maintaining two systems: the old Elementor and the new block-based version. Forever.
The future is Gutenberg
WordPress chose a direction. Gutenberg is the editor. Full Site Editing is how themes work. theme.json is how styles are managed.
You can fight that, or you can build with it.
Elementor had a good run. It solved a real problem when WordPress needed it. But the platform caught up. The page builder era is ending.
I’m building with Gutenberg. Clean markup. Semantic HTML. Global styles that actually work. Sites clients can edit without calling me. Sites that don’t depend on a plugin to exist.
That’s the future. It’s already here.
