Skip to main content

Outline

At a glance
  • Tables are stored HTML structures inside XhtmlString—enable intentionally.
  • Templates (TinyMCE template plugin) are a safer "known-good table" pattern.
  • Images have two insertion paths (toolbar vs drag/drop) that can produce different markup.
  • Alt text is easiest to govern via rendering/templates (or by converting drops to blocks).
  • Warning: don't break Optimizely's epi-dnd-processor by hijacking events.

Introduction

Tables and images look deceptively simple in an editor UI: one button inserts a table, another inserts an image, and the rest is "content." In Optimizely CMS 12 (PaaS), that simplicity is powered by a fairly opinionated integration between TinyMCE and Optimizely's content model.

The key implication for developers is this: tables and images are not just formatting. They affect accessibility, responsive behavior, and long-term maintainability. The choices you make in TinyMCE configuration determine whether editors produce consistent, design-system-aligned markup, or whether they accidentally create fragile HTML that breaks in production.

This module covers:

  • How to enable and govern table authoring (including "templates" as a safer alternative to free-form tables)
  • How image insertion works in CMS 12 (toolbar insert vs drag-and-drop)
  • How to handle alt text, editor drop behavior, and event hooks without breaking Optimizely's built-in drag-and-drop processor

1. Tables in CMS 12: What you're actually enabling

TinyMCE supports the table plugin, but CMS 12 does not enable it by default. Optimizely's default TinyMCE plugin list includes a small baseline set (help image fullscreen lists searchreplace anchor) and Optimizely's own integration plugins such as epi-link, epi-image-editor, epi-dnd-processor, and others. The default toolbar also reflects this: it includes image and epi-image-editor, but no table button.

This default matters. It tells you Optimizely's stance: tables are powerful, but also risky if your project doesn't have strong rules around responsiveness, styling, and semantic markup.

When you decide to add table support, you are deciding to let editors store structural HTML (<table>, <tr>, <td>) inside an XhtmlString. That can be perfectly valid — but it should be intentional.

Practical decision rule

Use tables in TinyMCE when:

  • Editors need lightweight tabular layouts (e.g., pricing matrices, comparison grids)
  • Your frontend has a stable table component/style, and you can enforce consistent classing
  • You accept that the table's HTML is content (stored and versioned), not "templated" rendering

Prefer blocks/components when:

  • You need responsiveness rules (stacking, horizontal scrolling, sticky headers)
  • You need validation (required columns, numeric formats)
  • The table structure changes frequently (you want to update rendering globally)

2. Enabling tables: Plugins and toolbar

In CMS 12, enabling table tooling is a combination of enabling the TinyMCE table plugin and exposing table controls on the toolbar (and optionally the menubar).

The Optimizely property configuration documentation provides a clear "kitchen sink" example that includes the table plugin and a toolbar row containing table.

Example: Add table support to a property

services.Configure<TinyMceConfiguration>(config => { config.For<ProductPage>(p => p.MainBody) .AddPlugin("table") .AppendToolbar("table"); });

This approach is additive. You keep existing plugins and simply layer table functionality on top.

Example: A fully governed configuration (plugins + toolbar rows)

If you want stricter control, you can clear plugins and explicitly add only what you need. This is also useful for debugging when "a button doesn't show."

services.Configure<TinyMceConfiguration>(config => { config.For<ProductPage>(p => p.MainBody) .Menubar("file edit insert view format table tools help") .ClearPlugins() .AddPlugin(@"epi-link epi-image-editor epi-dnd-processor epi-personalized-content fullscreen image link media template table lists searchreplace anchor help") .Toolbar( "epi-link | epi-image-editor | fullscreen", "styleselect formatselect | bold italic | bullist numlist | removeformat", "table"); });

Implementation notes:

  • AddPlugin(...) adds to an existing configuration; ClearPlugins() + AddPlugin(...) replaces the list.
  • Toolbar(...) overwrites previous toolbar rows; use AppendToolbar(...) if you are extending.

3. Table governance: The "template" pattern

Even with table tooling enabled, the biggest real-world issue is not "can editors insert a table?" It's that editors often create tables with inconsistent structure, missing headers, weird merges, or styling hacks.

A practical compromise is to avoid free-form table creation and instead give editors a set of pre-built table templates. A commonly used implementation pattern uses TinyMCE's template plugin to provide a menu of predefined HTML table structures.

The idea:

  • Developers define a controlled list of HTML templates.
  • Editors insert a template and then only fill in values.
  • TinyMCE detects the presence of a table and automatically exposes table tools for editing.
Key rule

If you use the template plugin, your setting name must be templates and the plugin name must be template.

Example: Enable the template button and plugin

services.Configure<TinyMceConfiguration>(config => { var simpleConfiguration = config.Default().Clone() .AddPlugin("template") .Toolbar("bold italic | undo redo | template"); config.For<YourPageType>(x => x.YourPropertyName, simpleConfiguration); });
Governance caveat

Templates are copied into the stored HTML at insertion time. Updating a template later does not retroactively update existing content.

4. Image handling in CMS 12: Two very different paths

In CMS 12 TinyMCE, editors commonly insert images in two ways: using the toolbar image feature (via the image plugin and Optimizely integration) or dragging an image from Optimizely's media pane and dropping it into the editor.

These two paths may produce different HTML. In the "insert image" flow, editors can often supply alt text via the dialog. In the drag-and-drop flow, the resulting <img> tag may default to file name based values unless you add customization.

5. Why alignment/padding options disappeared (and why that's good)

A common question coming from older versions is: "Why can't I set alignment or padding on the image in the editor UI anymore?"

A frequently cited explanation is that, for HTML5 sites, the older UI fields for padding/alignment were not an ideal authoring mechanism and were effectively removed as part of modern editor behavior. The modern approach is to let editors place the image and let developers control layout, spacing, and responsive alignment via CSS and components.

This is a good architectural framing: image layout belongs to the design system, not to one-off per-instance pixel tweaks inside a WYSIWYG.

6. Alt text: The real-world problem and practical options

Alt text is where "image handling" stops being cosmetic and becomes a product-quality concern.

A recurring issue in CMS implementations is that drag-and-drop insertions can default an image's alt value to the filename or a name-based value, which is rarely appropriate for accessibility.

Option A: Solve at rendering via Image display templates (best baseline)

A strong baseline is ensuring your image rendering view outputs alt text based on your image content model, rather than relying on whatever ended up inside the stored HTML.

If images render via @Html.PropertyFor(...) and your solution uses image display templates, you can enforce consistent alt text by aligning templates with your image content type properties.

Option B: Convert drops into "content blocks" using EditorDropBehavior

A robust approach is to change what happens when an editor drops an image into TinyMCE. Instead of inserting a raw <img>, the image can be inserted like a content block so rendering is fully controlled by templates.

[UIDescriptorRegistration] public class ImageFileUIDescriptor : UIDescriptor<ImageFile>, IEditorDropBehavior { public ImageFileUIDescriptor() { EditorDropBehaviour = EditorDropBehavior.CreateContentBlock; } public EditorDropBehavior EditorDropBehaviour { get; set; } }

This pattern helps ensure:

  • Drag-and-drop images behave like blocks
  • Rendering goes through your templates
  • Alt text can be sourced consistently from the image content model

Option C: Transform the XhtmlString markup (use sparingly)

Some teams parse and rewrite HTML output (for example, rewriting <img> attributes during rendering). This can work, but it is brittle and tends to accumulate maintenance cost. Treat it as a last resort if you cannot influence authoring behavior or templates.

7. Event hooks: Reacting to drops without breaking Optimizely's DnD

If you need to react to drag/drop events (for example, to detect when an editor drops an image), a practical approach is to implement a custom TinyMCE plugin and register it through the CMS configuration.

Example: Create a TinyMCE plugin listening to drag/drop

define(["tinymce"], function (tinymce) { tinymce.PluginManager.add("dnd-events", function (editor) { editor.on("dragover drop dragstart dragend", function (e) { console.log("TinyMCE DnD event:", e.type, e); }); }); });

Register the plugin in CMS 12

services.Configure<TinyMceConfiguration>(config => { config.Default() .AddExternalPlugin("dnd-events", "/alloy/plugins/dnd-events.js") .AddPlugin("dnd-events"); });
Important warning

Optimizely ships an epi-dnd-processor plugin required for dragging Optimizely content (blocks/pages) into TinyMCE. If your handlers prevent default behavior or stop propagation, you can break built-in drag-and-drop workflows.

Conclusion

Adding table and image handling in Optimizely CMS 12 (PaaS) is not a matter of sprinkling a few toolbar buttons. It is an authoring architecture decision.

Tables require governance. The simplest implementation is enabling table and exposing a button. A safer approach is providing templates so editors begin from known-good structures.

Images require clarity on insertion paths. Toolbar insert and drag/drop can produce different HTML, especially for alt text. The most robust solutions bring image rendering back under developer control — either by using image display templates consistently, or by converting drops into content blocks via EditorDropBehavior.

If deeper customization is required, TinyMCE can be extended with plugins registered via the CMS 12 configuration API — but you must respect Optimizely's built-in drag-and-drop processor to avoid breaking core editing workflows.