Profile
Back to NewsBack
GitHub Trending 30 min
Reader Mode
Mati365/ckeditor5-phoenix: CKEditor 5 for Phoenix - rich text editor for Elixir apps!  Easy setup, supports live-view data binding, dynamic loading, and localization. Plug-and-play modules, JS hooks, and full customization support. Ready for both ope

Mati365/ckeditor5-phoenix: CKEditor 5 for Phoenix - rich text editor for Elixir apps! Easy setup, supports live-view data binding, dynamic loading, and localization. Plug-and-play modules, JS hooks, and full customization support. Ready for both ope

11 hours ago

CKEditor 5 Phoenix Integration

License: MIT</a> PRs Welcome</a> !GitHub code size in bytes GitHub issues</a> Elixir Coverage</a> TS Coverage</a> !NPM Version !Hex.pm Version

CKEditor 5 integration library for Phoenix (Elixir) applications. Provides web components and helper functions for seamless editor integration with support for classic, inline, balloon, and decoupled editor types.

[!IMPORTANT]
This package is unofficial and not maintained by CKSource. For official CKEditor 5 documentation, visit ckeditor.com. If you encounter any issues in the editor, please report them on the GitHub repository.

CKEditor 5 Classic Editor in Phoenix (Elixir) application

Table of Contents

- Table of Contents - Installation 🚀 - 🔗 Compatibility - 🏠 Self-hosted - 📡 CDN Distribution - Basic Usage 🏁 - Configuration ⚙️ - Custom Presets 🧩 - Dynamic presets 🎯 - Providing the License Key 🗝️ - Referencing DOM Elements in Config 🏷️ - Editor Types 🖊️ - Classic editor 📝 - Multiroot editor 🌳 - Inline editor 📝 - Balloon editor 🎈 - Decoupled editor 🌐 - Paragraph-like editing 📄 - Classic / Balloon / Inline editor - Multiroot editor - Localization 🌍 - UI language and content language 🈯 - Global Translation Config 🛠️ - Custom translations 🌐 - Translation references 📝 - LiveView Sync 🔄 - Two-way Communication 🔄 - From Phoenix to JavaScript (Server → Client) 📥 - From JavaScript to Phoenix (Client → Server) 📤 - Multiroot editor 🌲 - Root attributes 🏷️ - Focus and blur events 👁️‍🗨️ - Ready event ✅ - Forms Integration 🧾 - Phoenix Form Helper 🧑‍💻 - LiveView Handler ⚡ - Image Upload 🖼️ - Enabling uploads 🚀 - Base64 Uploads 🖼️ - Backend Handling 📥 - Using Built-in Controller 📦 - Custom Controller 🛠️ - CSRF Protection 🛡️ - Custom plugins 🧩 - Context 🤝 - Basic usage 🔧 - Custom context translations 🌐 - Watch registered editors 👀 - Wait for particular editor to be registered ⏳ - Run logic on editor initialization and restarts 🔄 - Package development 🛠️ - Psst... 👀 - Trademarks 📜 - License 📜

Installation 🚀

Choose between two installation methods based on your needs. Both approaches provide the same functionality but differ in how CKEditor 5 assets are loaded and managed.

🔗 Compatibility

| CKEditor 5 Version | Integration Version | |--------------------|---------------------| | 43.x – 47.x | <= 1.26.x | | 48.x | <= 1.28.x | | >= 49.0 | >= 1.29.x |

🏠 Self-hosted

Bundle CKEditor 5 with your application for full control over assets, custom builds, and offline support. This method is recommended for advanced users or production applications with specific requirements. It's also GPL-compliant.

Complete setup:

  1. Add dependency to your mix.exs:
def deps do
     [
       {:ckeditor5_phoenix, "~> 1.28.2"}
     ]
   end
  1. Install CKEditor 5
mix ckeditor5.install # --premium --version 49.0.0
   # ... or: npm install ckeditor5 --prefix assets
  1. Add ckeditor5.install to assets.setup in mix.exs (if using Mix installer):
"assets.setup": ["ckeditor5.install", ... ]
  1. Register JavaScript hook in your app.js:
import { Hooks } from 'ckeditor5_phoenix';

const liveSocket = new LiveSocket('/live', Socket, { hooks: Hooks, });

  1. Import styles in your assets/css/app.css:
@import "../../deps/ckeditor5/dist/ckeditor5.css";
   / ... or: @import "../node_modules/ckeditor5/dist/ckeditor5.css"; /
  1. Import module in View
defmodule MyAppWeb.PageHTML do
     # ... your other uses
     use CKEditor5
   end
  1. Use in templates (no CDN assets needed):
<.ckeditor id="editor" type="classic" value="<p>Hello world!</p>" />
[!NOTE]
Make sure you use --splitting and --format=esm options in your esbuild configuration. It'll allow package to lazy load CKEditor 5.

📡 CDN Distribution

Load CKEditor 5 directly from CKSource's CDN - no build configuration required. This method is ideal for most users who want quick setup and don't need custom builds.

Complete setup:

  1. Add dependency to your mix.exs:
def deps do
     [
       {:ckeditor5_phoenix, "~> 1.28.2"}
     ]
   end
  1. Register JavaScript hook in your app.js:
import { Hooks } from 'ckeditor5_phoenix';

const liveSocket = new LiveSocket('/live', Socket, { hooks: Hooks, });

  1. Exclude CKEditor from bundler in your config/config.exs:
config :my_app, MyAppWeb.Endpoint,
     watchers: [
       esbuild: {Esbuild, :install_and_run, [
         :my_app,
         ~w(--external:ckeditor5 --external:ckeditor5-premium-features)
       ]}
     ]
  1. Add license key (see Providing the License Key 🗝️ section)
  1. Import module in View
defmodule MyAppWeb.PageHTML do
     # ... your other uses
     use CKEditor5
   end
  1. Use in templates:
<%!-- Load CDN assets in <head> (based on default preset) --%>
   <.cke_cloud_assets />

<%!-- or with specific features (overrides default preset) --%> <.cke_cloud_assets translations={["pl", "de", "fr"]} premium />

<%!-- or with specific preset --%> <.cke_cloud_assets preset="inline" />

<%!-- Use editor anywhere in <body> --%> <.ckeditor id="editor" type="classic" value="<p>Hello world!</p>" />

That's it! 🎉

Basic Usage 🏁

Render the <.ckeditor> component anywhere in your template. While most props are optional, setting an explicit id is recommended if you plan to reference the editor instance from JavaScript via the EditorsRegistry (e.g. to read content, attach listeners, or wait for initialization).

<%!-- CDN only: Load assets in <head> --%>
<.cke_cloud_assets />

<.ckeditor id="editor" <!-- unique ID; auto-generated with "cke-" prefix if omitted --> type="classic" <!-- classic | inline | balloon | decoupled | multiroot --> preset="default" <!-- preset name from config, or a %CKEditor5.Preset{} struct --> value="<p>Hello world!</p>" <!-- initial HTML content --> editable_height="300px" <!-- fixed height; editor grows with content if omitted --> language="pl" <!-- UI language (toolbar, dialogs) --> content_language="pl" <!-- lang attr on the editable area; defaults to language --> save_debounce_ms={300} <!-- debounce in ms for syncing content (default: 400) --> upload_url="/uploads" <!-- image upload endpoint; "base64" for inline Base64 adapter --> change_event={true} <!-- push ckeditor5:change to LiveView on content change --> root_attrs={%{}} <!-- root element attributes --> root_model_element="$root" <!-- root element name (default: $root) --> focus_event={true} <!-- push ckeditor5:focus to LiveView on focus --> blur_event={true} <!-- push ckeditor5:blur to LiveView on blur --> ready_event={true} <!-- push ckeditor5:ready once the editor is initialized --> class="my-editor" <!-- CSS classes on the outer container --> style="border: 1px solid #ccc" <!-- inline styles on the outer container --> />

Configuration ⚙️

You can configure the editor _presets_ in your config/config.exs file. The default preset is :default, which provides a basic configuration with a toolbar and essential plugins — you can browse its full definition presets.ex. The preset is a map that contains the editor configuration, including the toolbar items and plugins. There can be multiple presets, and you can switch between them by passing the preset keyword argument to the ckeditor component.

Custom Presets 🧩

In order to override the default preset or add custom presets, you can add the following configuration to your config/config.exs file:

# config/config.exs
config :ckeditor5_phoenix,
  presets: %{
    minimal: %{
      cloud: %{
        version: "46.0.0",
        premium: true,
        translations: ["pl"],
        ckbox: %{
          version: "1.0.0"
        }
      },
      config: %{
        toolbar: [:bold, :italic, :link],
        plugins: [:Bold, :Italic, :Link, :Essentials, :Paragraph]
      }
    },
    full: %{
      config: %{
        toolbar: [
          :heading, :|, :bold, :italic, :underline, :|,
          :link, :insertImage, :insertTable, :|,
          :bulletedList, :numberedList, :blockQuote
        ],
        plugins: [
          :Heading, :Bold, :Italic, :Underline, :Link,
          :ImageBlock, :ImageUpload, :Table, :List, :BlockQuote,
          :Essentials, :Paragraph
        ]
      }
    }
  }

In template:

<.ckeditor preset="minimal" value="<p>Simple editor</p>" />

Dynamic presets 🎯

You can also create dynamic presets that can be modified at runtime. This is useful if you want to change the editor configuration based on user input or other conditions.

defmodule MyApp.PageLive do
  use MyAppWeb, :live_view
  use CKEditor5

alias CKEditor5.Preset

def mount(_params, _session, socket) do preset = Preset.Parser.parse!(%{ config: %{ toolbar: [:bold, :italic, :link], plugins: [:Bold, :Italic, :Link, :Essentials, :Paragraph] } })

{:ok, assign(socket, preset: preset)} end end

In template:

<.ckeditor preset={@preset} />

Providing the License Key 🗝️

CKEditor 5 requires a license key when using the official CDN or premium features. You can provide the license key in two simple ways:

  1. Environment variable: Set the CKEDITOR5_LICENSE_KEY environment variable before starting your Phoenix app. This is the easiest and most common way.
  2. Preset config: You can also set the license key directly in your preset configuration in config/config.exs:
config :ckeditor5_phoenix,
     presets: %{
       default: %{
         license_key: "your-license-key-here"
       }
     }

If you use CKEditor 5 under the GPL license, you do not need to provide a license key. However, if you choose to set one, it must be set to GPL.

If both are set, the preset config takes priority. For more details, see the CKEditor 5 licensing guide.

Referencing DOM Elements in Config 🏷️

You can reference DOM elements directly in your editor configuration using the special { $element: "selector" } format. This is useful when you want to attach the editor's UI parts (like toolbars or editable areas) to specific elements in your HTML.

# config/config.exs
config :ckeditor5_phoenix,
  presets: %{
    # ... other presets
    minimal: %{
      config: %{
        # ... other config
        yourPlugin: %{
          toolbar: %{ $element: "#my-toolbar" },
          editable: %{ $element: "#my-editable" }
        },
      }
    }
  }

This will find the elements with IDs my-toolbar and my-editable in the DOM and use them for the editor's UI. If the element is not found, a warning will be shown in the console.

Editor Types 🖊️

CKEditor 5 Phoenix supports four distinct editor types, each designed for specific use cases. Choose the one that best fits your application's layout and functionality requirements.

Classic editor 📝

Traditional WYSIWYG editor with a fixed toolbar above the editing area. Best for standard content editing scenarios like blog posts, articles, or forms.

!CKEditor 5 Classic Editor in Elixir Phoenix application with Menubar

<%!-- CDN assets in <head> --%>
<.cke_cloud_assets />

<%!-- Classic editor in <body> --%> <.ckeditor type="classic" value="<p>Initial content here</p>" editable_height="300px" />

Multiroot editor 🌳

Advanced editor supporting multiple independent editable areas within a single editor instance. Perfect for complex layouts like page builders, newsletters, or multi-section content management.

!CKEditor 5 Multiroot Editor in Elixir Phoenix application

<%!-- CDN assets in <head> --%>
<.cke_cloud_assets />

<%!-- Editor container --%> <.ckeditor type="multiroot" />

<%!-- Shared toolbar --%> <.cke_ui_part name="toolbar" />

<%!-- Multiple editable areas --%> <div class="flex flex-col gap-4"> <.cke_editable root="header" value="<h1>Main Header</h1>" class="border border-gray-300" /> <.cke_editable root="content" value="<p>Main content area</p>" class="border border-gray-300" /> <.cke_editable root="sidebar" value="<p>Sidebar content</p>" class="border border-gray-300" /> </div>

Inline editor 📝

Minimalist editor that appears directly within content when clicked. Ideal for in-place editing scenarios where the editing interface should be invisible until needed.

!CKEditor 5 Inline Editor in Elixir Phoenix application

<%!-- CDN assets in <head> --%>
<.cke_cloud_assets />

<%!-- Inline editor --%> <.ckeditor type="inline" value="<p>Click here to edit this content</p>" editable_height="300px" />

Note: Inline editors don't work with