🧩 Framework Components
Tinymce Customization
📖 Docs

TinyMCE Customization - Optimizely CMS 12

Exam Area: Area 4 – Framework Components (15%)
Source: https://academy.optimizely.com/student/path/3128969/activity/4970338 | /4970339 | /4970342
Published: Feb 16–17, 2026


Overview (Academy Key Points)

  • Where TinyMCE shows up: Automatically for XhtmlString properties in the CMS UI.
  • Primary lever: Centralized configuration via DI using IConfigureOptions<TinyMceConfiguration>.
  • Scope hierarchy: Default() (global) → content type → property-specific (most specific wins).
  • Toolbar = what editors see; valid_elements = what HTML can be stored.
  • PaaS reality: Configuration is code-driven, promoted via CI/CD (treat changes like schema evolution).

1. TinyMCE in CMS 12 (Academy – 4970338)

TinyMCE is the default editor for XhtmlString properties. Integration is automatic — no View configuration required.

// XhtmlString property → TinyMCE auto-attached in Edit mode
[Display(Name = "Main body", GroupName = SystemTabNames.Content, Order = 100)]
public virtual XhtmlString MainBody { get; set; }

2. IConfigureOptions Pattern (Academy – 4970338/4970339)

using EPiServer.Cms.TinyMce.Core;
using Microsoft.Extensions.Options;

public class ConfigureTinyMceOptions : IConfigureOptions<TinyMceConfiguration>
{
    public void Configure(TinyMceConfiguration config)
    {
        config.Default()
            .AddPlugin("lists")
            .AddPlugin("link")
            .Toolbar("bold italic | bullist numlist | link");
    }
}
// Registration in Program.cs
builder.Services.ConfigureOptions<ConfigureTinyMceOptions>();

3. Scoped Configuration – Global, Content-type, Property (Academy – 4970339)

// Global default — applies to ALL XhtmlString properties
config.Default()
    .Toolbar("bold italic underline")
    .AddPlugin("link");

// Content-type specific — applies to all XhtmlString in StandardPage
config.For<StandardPage>()
    .Toolbar("bold italic | link");

// Property-level — most specific, overrides everything above
config.For<StandardPage>(p => p.MainBody)
    .Toolbar("bold italic underline | link image");

// Summary field — minimal
config.For<ArticlePage>(p => p.Summary)
    .Toolbar("bold italic | epi-link");

Resolution order: Global Default → Content-type → Property (most specific wins).


4. Plugin Management (Academy – 4970339)

config.Default()
    .AddPlugin("code")
    .AddPlugin("table")
    .AddPlugin("lists")
    .AddPlugin("image")
    .Toolbar("bold italic | table | code");

Rule: A toolbar button requires its plugin to be enabled — adding a button without the plugin is a no-op.


5. Toolbar Customization with AppendToolbar (Academy – 4970342)

services.Configure<TinyMceConfiguration>(config =>
{
    config.Default()
        .AddEpiserverSupport()
        .Toolbar("formatselect styleselect | bold italic underline | epi-link image")
        .AppendToolbar("code table");
});

6. StyleFormats – Mapping Styles to Frontend CSS (Academy – 4970342)

config.For<StandardPage>(t => t.MainBody)
    .Toolbar("styleselect | bold italic")
    .StyleFormats(
        new {
            title = "Lead Paragraph",
            block = "p",
            classes = "lead"
        },
        new {
            title = "Callout",
            block = "div",
            classes = "callout-box"
        },
        new {
            title = "Important",
            inline = "span",
            classes = "text-important"
        }
    );

Note: Every CSS class referenced in StyleFormats must exist in the frontend stylesheet. StyleFormats() replaces default styles — merge explicitly if needed.


7. valid_elements – Restrict Allowed HTML (Academy – 4970339)

config.Default()
    .AddSetting("valid_elements", "p,strong/b,em/i,ul,ol,li,a[href|target]");

Restricts stored HTML to specific elements — essential for structured design systems.


8. Content CSS / BodyClass

config.Default()
    .ContentCss("/Static/css/editor.css")
    .BodyClass("mce-content-body");

9. Property-level Attribute Approach (Academy – 4970339)

[TinyMceSettings(
    Toolbar = "bold italic underline",
    Plugins = "lists link")]
public virtual XhtmlString Summary { get; set; }

Use sparingly — centralized IConfigureOptions is preferred for large solutions.


10. Style Format Localization (Academy – 4970342)

<!-- /lang/en.xml -->
<languages>
  <language name="English" id="en">
    <tinymce>
      <editorstyles>
        <lead-paragraph>Lead paragraph</lead-paragraph>
        <callout>Callout</callout>
      </editorstyles>
    </tinymce>
  </language>
</languages>

Review Questions

  1. For which property type is TinyMCE activated automatically? (XhtmlString)
  2. What interface pattern is used to configure TinyMCE? (IConfigureOptions<TinyMceConfiguration>)
  3. What is the scope resolution order for TinyMCE config? (Global Default → Content-type → Property)
  4. How does AppendToolbar() differ from Toolbar()? (Toolbar() replaces; AppendToolbar() extends)
  5. What does StyleFormats() do? (Defines structured formatting options mapped to frontend CSS classes)
  6. What is the valid_elements setting used for? (Restrict allowed HTML tags/attributes in stored content)
  7. Why must TinyMCE config changes be deployed in PaaS? (Config is code-driven — no runtime UI changes are possible)