Skip to main content

Storage model: JSON, not shortcodes

A criticism regularly aimed at any page builder from the Classic Editor era goes like this:

Classic builders translate your layout into hundreds of nested shortcodes saved into post_content. WordPress re-parses those brackets on every request, which hurts TTFB. And if you ever deactivate the builder, your database is left full of broken brackets.

That describes a real family of builders. It does not describe this one, and the difference is checkable in about thirty seconds — so this page states what is actually stored, then says plainly which parts of the criticism do land.

What is actually stored

Measured on a builder page in a running install:

builder JSON (post_meta) : 52,891 bytes
starts: [{"type":"section","_items":[{"type":"column","width":"1_1", …

post_content : 17,583 bytes
shortcode tags : 0

The layout lives in post meta, under fw:opt:ext:pb:page-builder:json, as a JSON tree of typed items. post_content contains no shortcode tags at all.

You can verify it on any site:

SELECT LEFT(meta_value, 120) FROM wp_postmeta
WHERE meta_key = 'fw:opt:ext:pb:page-builder:json' LIMIT 1;

Shortcodes are a rendering step, not the storage format

Shortcodes do exist in the pipeline — they are generated from the JSON at render time, by FW_Option_Type_Page_Builder::json_to_shortcodes(), and immediately turned into HTML. They are an intermediate representation inside a single request, not something written to the database. The full mechanics are in Data flow: edit → stored JSON → render.

That distinction is what makes the criticism miss:

The claimReality here
"Shortcode soup fills your database"The database holds a JSON tree. post_content carries no shortcode tags.
"Deactivate and you're left with thousands of broken brackets"There are no brackets to leave behind.
"WordPress re-parses brackets every load, hurting TTFB"Rendering starts from a parsed JSON tree, not from bracket-scanning post_content.
"Modern tools parse modular JSON objects instead"That is precisely the model in use.

The last row is the interesting one: storing structured JSON rather than markup is the criterion usually offered to define a modern builder, and it is the criterion this architecture already meets.

Structured storage buys more than a rebuttal. Because the layout is data rather than markup, it can be queried, diffed, migrated between value shapes, generated by the Site Converter, and re-rendered by a different renderer entirely — which is exactly how the same content appears through a Gutenberg block without a second implementation.

What the criticism does get right

Being straight about this matters more than winning the argument, and these are on the modernization plan:

  • The builder canvas is imperative. Backbone + jQuery, not a reactive state engine. Storage is modern; the editing surface is the older layer.
  • The Live Editor re-renders over the network. It uses AJAX and an iframe rather than a client-side state engine, so it behaves like the "legacy live editing" the criticism describes.
Editing latency: measured, and it wasn't architectural

An earlier version of this page listed editing performance as a structural weakness. Measuring it showed otherwise, and the correction is worth keeping visible.

Opening an element's options modal cost ~6.7 MB and ~873 ms — but almost none of that was the framework's design. One tab (Animations) was 95% of it, because a picker control rendered every choice's sub-options eagerly, including the ~40 the user hadn't selected and that the save path never reads.

Deferring those unread variants took it to ~2.1 MB / ~137 ms — the same architecture, 6× faster, and 13× on the offending tab. It was a bug in one control, not a limit of server-rendered options.

The general lesson generalises past this framework: "the admin feels slow" gets attributed to a stack's age far more often than anyone profiles it.

  • The builder mounts on the Classic Editor screen. A real coupling to a WordPress surface being de-emphasised over time.
  • DOM depth is middling. A section renders roughly section > .fw-container > .fw-row > … — about four levels. Leaner than the seven-deep wrappers of the builders usually cited, but not the two-or-three-tag ideal.

One further criterion already met: generated CSS is written to static files under uploads/unysonplus/css/, not injected inline per page.

Judging a builder on more than one axis

"Modern" gets used as a single verdict when it is really four independent questions:

AxisWhere this framework sits
Storage modelStructured JSON in post meta — strong
Output qualityClean semantic HTML, static generated CSS, jQuery-free front end — good
Editing performanceImperative canvas and a network-backed live editor; modal render is ~137 ms after profiling — the least modern axis, but not the bottleneck it looked like
Coupling to coreMounted on the Classic Editor screen — a real dependency

Those axes are not equally reversible, which is the point. An editing canvas can be replaced; stored content cannot. A builder with a React canvas that writes shortcode markup into post_content has locked in the part that is expensive to change and modernised the part that is cheap. This framework did the opposite — which is why the roadmap targets the canvas and leaves the storage model alone.