CKEditor 5 Phoenix Integration
!GitHub code size in bytes
!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.
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:
- Add dependency to your
mix.exs:
def deps do
[
{:ckeditor5_phoenix, "~> 1.28.2"}
]
end
- Install CKEditor 5
mix ckeditor5.install # --premium --version 49.0.0
# ... or: npm install ckeditor5 --prefix assets
- Add
ckeditor5.installtoassets.setupinmix.exs(if using Mix installer):
"assets.setup": ["ckeditor5.install", ... ]
- Register JavaScript hook in your
app.js:
import { Hooks } from 'ckeditor5_phoenix';
const liveSocket = new LiveSocket('/live', Socket, {
hooks: Hooks,
});
- Import styles in your
assets/css/app.css:
@import "../../deps/ckeditor5/dist/ckeditor5.css";
/ ... or: @import "../node_modules/ckeditor5/dist/ckeditor5.css"; /
- Import module in View
defmodule MyAppWeb.PageHTML do
# ... your other uses
use CKEditor5
end
- Use in templates (no CDN assets needed):
<.ckeditor id="editor" type="classic" value="<p>Hello world!</p>" />
[!NOTE]
Make sure you use--splittingand--format=esmoptions in youresbuildconfiguration. 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:
- Add dependency to your
mix.exs:
def deps do
[
{:ckeditor5_phoenix, "~> 1.28.2"}
]
end
- Register JavaScript hook in your
app.js:
import { Hooks } from 'ckeditor5_phoenix';
const liveSocket = new LiveSocket('/live', Socket, {
hooks: Hooks,
});
- 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)
]}
]
- Add license key (see Providing the License Key 🗝️ section)
- Import module in View
defmodule MyAppWeb.PageHTML do
# ... your other uses
use CKEditor5
end
- 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:
- Environment variable: Set the
CKEDITOR5_LICENSE_KEY environment variable before starting your Phoenix app. This is the easiest and most common way.
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