CKEditor 5 Phoenix Integration โจ
View SourceCKEditor 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
- CKEditor 5 Phoenix Integration โจ
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.
๐ 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.21.0"} ] endInstall CKEditor 5
mix ckeditor5.install # --premium --version 47.6.0 # ... or: npm install ckeditor5 --prefix assetsAdd
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 endUse in templates (no CDN assets needed):
<.ckeditor id="editor" type="classic" value="<p>Hello world!</p>" />
๐ก 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.21.0"} ] endRegister 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 endUse 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 ๐
Get started with the most common usage patterns. These examples show how to render editors in your templates and handle real-time content changes.
Simple Editor โ๏ธ
Create a basic editor with default toolbar and features. Perfect for simple content editing without server synchronization.
<%!-- CDN only: Load assets in <head> --%>
<.cke_cloud_assets />
<%!-- Render editor with initial content --%>
<.ckeditor
id="editor"
type="classic"
value="<p>Initial content</p>"
editable_height="300px"
/>Watchdog prop ๐ถ
By default, the <.ckeditor> component uses a built-in watchdog mechanism to automatically restart the editor if it crashes (e.g., due to a JavaScript error). The watchdog periodically saves the editor's content and restores it after a crash, minimizing the risk of data loss for users.
The watchdog is enabled by default. To disable it, set the watchdog prop to false:
<.ckeditor
type="classic"
value="<p>Initial content</p>"
watchdog={false}
/>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. 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
endIn 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](https://ckeditor.com/docs/ckeditor5/latest/getting-started/licensing/license-and-legal.html).
### 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.

<%!-- 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.

<%!-- 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.

<%!-- 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 `<textarea>` elements and may not be suitable for traditional form scenarios.
### Balloon editor ๐
Contextual editor that shows a floating toolbar near the selected text. Great for editing small content snippets or when you want to minimize UI clutter.

<%!-- CDN assets in <head> --%> <.cke_cloud_assets />
<%!-- Balloon editor --%> <.ckeditor type="balloon" value="<p>Click here to edit this content</p>" editable_height="300px" />
### Decoupled editor ๐
Flexible editor where toolbar and editing area are completely separated. Provides maximum layout control for custom interfaces and complex applications.

<%!-- CDN assets in <head> --%> <.cke_cloud_assets />
<%!-- Decoupled editor container --%> <.ckeditor id="your-editor" type="decoupled"> <div class="flex flex-col gap-4"> <%!-- Toolbar can be placed anywhere --%> <.cke_ui_part name="toolbar" />
<%!-- Editable area with custom styling --%> <.cke_editable value="<p>Initial content here</p>" class="border border-gray-300 p-4 rounded" editable_height="300px" /> </div> </.ckeditor>
## Localization ๐
Support multiple languages in the editor UI and content. Learn how to load translations via CDN or configure them globally.
### UI language and content language ๐ฏ
Use `language` to set the CKEditor UI language (menus, tooltips, labels) and `content_language` to set the language of the editable content (`lang` attribute in the editor area).
<.cke_cloud_assets translations={["pl"]} />
<.ckeditor id="article-editor" type="classic" language="pl" content_language="en" value="<p>Hello world!</p>" />
### Global Translation Config ๐ ๏ธ
You can also configure translations globally in your `config/config.exs` file. This is useful if you want to load translations for multiple languages at once or set a default language for the editor. Keep in mind that this configuration is only used when loading translations via CDN. If you are using self-hosted setup, translations are handled by your bundler automatically.
# config/config.exs config :ckeditor5_phoenix, presets: %{ default: %{ cloud: %{ translations: ["pl", "de", "fr"] # CDN only } } }
**Note:** For self-hosted setups, translations are handled by your bundler automatically.
### Custom translations ๐
You can also provide custom translations for the editor. This is useful if you want to override existing translations or add new ones. Custom translations can be provided in the preset configuration.
# config/config.exs config :ckeditor5_phoenix, presets: %{ default: %{ custom_translations: %{ en: %{ Bold: "Custom Bold", Italic: "Custom Italic" }, pl: %{ Bold: "Pogrubiony", Italic: "Kursywa" } } } }
#### Translation references ๐
Beyond static strings, the configuration itself can reference translation keys using the special
`$translation` object. This is resolved at initialization using the same translation packs
loaded for the editor (including any custom translations) and is particularly handy when
configuring plugin labels, toolbar items or other strings that should adapt to the UI language.
# config/config.exs config :ckeditor5_phoenix, presets: %{ default: %{ custom_translations: %{ en: %{CustomPlugin: "Custom Bold"}, pl: %{CustomPlugin: "Custom Plugin"} }, config: %{ customPlugin: %{ $translation: "CustomPlugin" } } } }
When the editor/context is created the reference will be replaced with the translated value for
`language.ui`. If no translation is found for the active locale, `null` is used and a warning is
logged.
## LiveView Sync ๐
Enable real-time synchronization between the editor and your LiveView. Content changes are automatically sent to the server with configurable debouncing for performance optimization.
### Two-way Communication ๐
CKEditor 5 Phoenix supports bidirectional communication between your LiveView server and the JavaScript editor instance. You can receive updates from the editor and programmatically control its content from your Elixir code.

#### From Phoenix to JavaScript (Server โ Client) ๐ฅ
There are two ways to update the editor content from your LiveView server:
1. **Reactive State Update (Recommended) โจ**
The simplest way to update the editor is to change the assign bound to the value attribute. LiveView handles the synchronization seamlessly without requiring manual event pushing.
```heex
<button phx-click="load_template">Load Template</button>
<.ckeditor id="editor" value={@editor_value} />
```
```elixir
defmodule MyAppWeb.EditorLive do
use MyAppWeb, :live_view
use CKEditor5
def mount(_params, _session, socket) do
{:ok, assign(socket, editor_value: "<h1>Initial Content</h1>")}
end
# Reactively updates the editor content by changing the assign
def handle_event("load_template", _params, socket) do
{:noreply, assign(socket, editor_value: "<h1>Daily Report</h1><p>Work is progressing well.</p>")}
end
end
```
2. **Imperative Update via `push_event` ๐**
If you prefer not to continuously track the editor's state in your assigns, or if you need to force an update directly, you can push a standard Phoenix event to the client. The integration listens for the `ckeditor5:set-data` event and requires the `editorId` and data payload.
```heex
<form phx-submit="force_set_data">
<textarea name="new_content" placeholder="Enter HTML content..."></textarea>
<button type="submit">Set Data</button>
</form>
```
```elixir
def handle_event("force_set_data", %{"new_content" => val}, socket) do
# Forces an update by pushing an event directly to the specific editor instance
{:noreply, push_event(socket, "ckeditor5:set-data", %{editorId: "editor", data: val})}
end
```
#### From JavaScript to Phoenix (Client โ Server) ๐ค
By adding the change_event attribute, the editor automatically sends content updates to your LiveView whenever the user types.
<.ckeditor id="editor" value={@content} change_event />
# Receives content updates from the editor def handle_event("ckeditor5:change", %{"data" => %{"main" => data}}, socket) do {:noreply, assign(socket, editor_value: data)} end
You can use `setData` event to programmatically update the editor content from JavaScript. It'll be automatically synchronized with Phoenix if `change_event` is enabled.
document.getElementById('update-button').addEventListener('click', async () => { const editor = await EditorsRegistry.the.get('editor');
editor.setData('<h1>New Content</h1><p>This content was set from JavaScript!</p>'); });
#### Multiroot editor ๐ฒ
For multiroot editors, reactivity works on a per-root basis. You can iterate over your roots and sync changes accordingly.

<.ckeditor type="multiroot" change_event />
<.cke_ui_part name="toolbar" class="mb-4" />
<div class="flex flex-col gap-6"> <%= for root <- @roots do %> <.cke_editable root={root.id} value={root.value} /> <% end %> </div>
def handle_event("ckeditor5:change", %{"data" => data}, socket) do updated_roots = Enum.map(socket.assigns.roots, fn root -> if Map.has_key?(data, root.id) do %{root | value: data[root.id]} else root end end)
end
### Focus and blur events ๐๏ธโ๐จ๏ธ
To handle focus and blur events, you can use the `focus_event` and `blur_event` attributes in the component. This allows you to capture when the editor gains or loses focus, which can be useful for tracking user interactions or saving content.

<.ckeditor id="editor" value={@content} focus_event blur_event />
def handle_event("ckeditor5:focus", %{"data" => data}, socket) do {:noreply, assign(socket, content: data["main"])} end
def handle_event("ckeditor5:blur", %{"data" => data}, socket) do {:noreply, assign(socket, content: data["main"])} end
These events are sent **immediately** when the editor gains or loses focus, allowing you to perform actions like saving content or updating UI elements.
### Ready event โ
Sometimes you need to know when the editor has finished initializing so you can perform setup work (e.g. enable UI controls or notify the user). The `ready_event` attribute will make the component push a `ckeditor5:ready` event **once** as soon as the editor is ready.
<.ckeditor id="editor" value={@content} ready_event />
def handle_event("ckeditor5:ready", %{"data" => data}, socket) do # editor is ready โ maybe mark a flag in assigns {:noreply, assign(socket, editor_ready?: true)} end
The payload contains the same root data object that change/focus/blur events include. This event is perfect for showing โeditor initializedโ messages or performing clientโside setup.
## Forms Integration ๐งพ
Seamlessly integrate CKEditor 5 with Phoenix forms and LiveView for robust content management. Learn how to handle form submissions and real-time updates.
### Phoenix Form Helper ๐งโ๐ป
The editor automatically creates hidden input fields for form integration. Content is synchronized with form fields using the `field` attribute, making it compatible with standard Phoenix form helpers.
<.form for={@form} phx-submit="save"> <.ckeditor id="content-editor" field={@form[:content]} />
<button type="submit">Save</button> </.form>
### LiveView Handler โก
Complete LiveView integration with event handling for both real-time updates and form processing.
defmodule MyApp.PageLive do use MyAppWeb, :live_view use CKEditor5 # Adds event handlers
def mount(_params, _session, socket) do form = to_form(%{"content" => ""}, as: :form) {:ok, assign(socket, form: form)} end
# Handle real-time content changes def handle_event("ckeditor5:change", %{"data" => data}, socket) do # Update content in real-time updated_params = Map.put(socket.assigns.form.params, "content", data["main"]) {:noreply, assign(socket, form: to_form(updated_params, as: :form))} end
# Handle form validation def handle_event("validate", %{"form" => params}, socket) do {:noreply, assign(socket, form: to_form(params, as: :form))} end
# Handle form submission def handleevent("save", %{"form" => params}, socket) do # Process and save form data case save_content(params) do {:ok, } -> {:noreply, putflash(socket, :info, "Content saved successfully!")} {:error, } -> {:noreply, put_flash(socket, :error, "Failed to save content")} end end end
## Image Upload ๐ผ๏ธ
CKEditor 5 Phoenix includes a dedicated upload adapter that integrates seamlessly with Phoenix applications. It handles image uploads using standard `multipart/form-data` requests and automatically includes CSRF tokens.
### Enabling uploads ๐
To enable image uploads, simply provide the `upload_url` prop to the editor component. This URL should point to your Phoenix controller action that handles the file upload.
<.ckeditor id="editor" upload_url="/api/uploads" value="<p>Content with images</p>" />
Alternatively, you can configure the upload URL globally in your `config/config.exs`:
config :ckeditor5_phoenix, :uploads, api_url: "/api/uploads"
When `upload_url` is configured (either via prop or globally), the editor will automatically:
1. Enable the `PhoenixUploadAdapter` plugin.
2. Disable conflicting adapters (like `SimpleUploadAdapter` or `Base64UploadAdapter`).
3. Send upload requests to the specified URL.
### Base64 Uploads ๐ผ๏ธ
If you prefer to store images as Base64 strings directly in the content (not recommended for production due to performance impact), you can set `upload_url` to `"base64"`.
<.ckeditor id="editor" upload_url="base64" value="<p>Content with images</p>" />
This will enable the `Base64UploadAdapter` and disable other upload adapters. It's default behavior if no `upload_url` is provided nor global configuration exists.
### Backend Handling ๐ฅ
The editor expects the server to return a JSON response containing the URL of the uploaded file:
{ "url": "https://example.com/images/foo.jpg" }
In case of an error, return a JSON object with an error message:
{ "error": { "message": "File too big." } }
#### Using Built-in Controller ๐ฆ
The package includes a ready-to-use `CKEditor5.Upload.Controller` that handles local file storage. To use it:
1. **Configure storage location**:
config :ckeditor5_phoenix, :uploads,
folder: "priv/static/uploads",
api_url: "/api/ckeditor5/uploads",
url: "/uploads" # Base URL for serving files
2. **Add route** in your `router.ex`:
scope "/api/ckeditor5" do
pipe_through :api # Ensure this pipeline expects JSON
post "/upload", CKEditor5.Upload.Controller, :uploadend
#### Custom Controller ๐ ๏ธ
If you need custom logic (e.g., uploading to S3), create your own controller. The `folder` and `url` config options are not needed in this case. However, make sure `api_url` is set correctly.
defmodule MyAppWeb.UploadController do use MyAppWeb, :controller
def upload(conn, %{"file" => upload}) do # 1. Handle the upload (e.g., store in S3) # 2. Return the public URL json(conn, %{url: "https://my-bucket.s3.amazonaws.com/..."}) end end
### CSRF Protection ๐ก๏ธ
The adapter automatically fetches the CSRF token from the `meta[name="csrf-token"]` tag or `_csrf_token` cookie and sends it in the `X-CSRF-Token` header.
However, standard Phoenix `:api` pipelines often **do not** include CSRF protection by default. To secure your upload endpoint, ensure your pipeline includes the verification plug:
pipeline :api do plug :accepts, ["json"] plug :fetch_session # Required for CSRF protection plug :protect_from_forgery end
## Custom plugins ๐งฉ
To register a custom plugin, use the `registerCustomEditorPlugin` function. This function takes the plugin name and the plugin _reader_ that returns a class extending `Plugin`.
import { CustomEditorPluginsRegistry as Registry } from 'ckeditor5_phoenix';
const unregister = Registry.the.register('MyCustomPlugin', async () => { // It's recommended to use lazy import to // avoid bundling ckeditor code in your application bundle. const { Plugin } = await import('ckeditor5');
return class extends Plugin { static get pluginName() { return 'MyCustomPlugin'; }
init() { console.log('MyCustomPlugin initialized'); // Custom plugin logic here } }; });
In order to use the plugin you need to extend your config in `config/config.exs`:
config :ckeditor5_phoenix, presets: %{ default: %{ config: %{ plugins: [:MyCustomPlugin, :Essentials, :Paragraph], # ... other config options } } }
It must be called before the editor is initialized. You can unregister the plugin later by calling the returned function:
unregister(); // or CustomEditorPluginsRegistry.the.unregister('MyCustomPlugin');
If you want to de-register all registered plugins, you can use the `unregisterAll` method:
import { CustomEditorPluginsRegistry } from 'ckeditor5_phoenix';
CustomEditorPluginsRegistry.the.unregisterAll();
## Context ๐ค
The **context** feature is designed to group multiple editor instances together, allowing them to share a common context. This is particularly useful in collaborative editing scenarios, where users can work together in real time. By sharing a context, editors can synchronize features such as comments, track changes, and presence indicators across different editor instances. This enables seamless collaboration and advanced workflows in your Phoenix application.
For more information about the context feature, see the [CKEditor 5 Context documentation](https://ckeditor.com/docs/ckeditor5/latest/features/collaboration/context-and-collaboration-features.html).

### Basic usage ๐ง
Define your context in configuration:
config :ckeditor5_phoenix, contexts: %{ "your-context" => %{ config: %{ plugins: [ :CustomContextPlugin ] }, watchdog: %{ crash_number_limit: 20 } } }, presets: %{ # ... }
And use it in your LiveView:
<.cke_context context="your-context"> <.ckeditor class="mb-6" value="Child A" /> <.ckeditor value="Child B" /> </.cke_context>
Voila!
> [!NOTE]
> The `context` attribute accepts also `CKEditor5.Context` structure, so it can be used in LiveView assigns or other dynamic contexts.
### Custom context translations ๐
Define your custom translations in the configuration:
config :ckeditor5_phoenix, contexts: %{ "custom" => %{ # ... custom_translations: %{ en: %{ Bold: "Custom Bold", Italic: "Custom Italic" }, pl: %{ Bold: "Pogrubiony", Italic: "Kursywa" } } } }
These translations will be used in the context's editors, overriding the default translations. They are available through `locale.t` plugin in every context plugin.
## Watch registered editors ๐
You can watch the registered editors using the `watch` function. This is useful if you want to react to changes in the registered editors, for example, to update the UI or perform some actions when an editor is added or removed.
import { EditorsRegistry } from 'ckeditor5_phoenix';
const unregisterWatcher = EditorsRegistry.the.watch((editors) => { console.log('Registered editors changed:', editors); });
// Later, you can unregister the watcher unregisterWatcher();
### Wait for particular editor to be registered โณ
You can also wait for a specific editor to be registered using the `waitForEditor` function. This is useful if you want to perform some actions after a specific editor is registered.
This method can be called before the editor is initialized, and it will resolve when the editor is registered.
import { EditorsRegistry } from 'ckeditor5_phoenix';
EditorsRegistry.the.waitFor('editor1').then((editor) => { console.log('Editor "editor1" is registered:', editor); });
// ... init editor somewhere later
The `id` of the editor must be used to identify the editor. If the editor is already registered, the promise will resolve immediately.
## Package development ๐ ๏ธ
In order to contribute to CKEditor 5 Phoenix or run it locally for manual testing, here are some handy commands to get you started.
To run the minimal Phoenix application with CKEditor 5 integration, install dependencies and start the server:
mix playground
In order to run the playground in cloud mode, set the `CKEDITOR5_PLAYGROUND_MODE` environment variable to `cloud`:
CKEDITOR5_PLAYGROUND_MODE=cloud mix playground
Run tests using the `mix test` command. All tests are located in the `test/` directory.
mix test
To generate a code coverage report, use:
mix coveralls.html
## Psst... ๐
Discover related projects for other frameworks and languages. Find inspiration or alternative integrations for CKEditor 5.
Looking for similar projects or inspiration? Check out these repositories:
- [ckeditor5-rails](https://github.com/Mati365/ckeditor5-rails)
Effortless CKEditor 5 integration for Ruby on Rails. Works seamlessly with standard forms, Turbo, and Hotwire. Easy setup, custom builds, and localization support.
- [ckeditor5-livewire](https://github.com/Mati365/ckeditor5-livewire)
Plug-and-play CKEditor 5 solution for Laravel + Livewire applications. Fully compatible with Blade forms. Includes JavaScript hooks, flexible configuration, and easy customization.
- [ckeditor5-symfony](https://github.com/Mati365/ckeditor5-symfony)
Native CKEditor 5 integration for Symfony. Works with Symfony 6.x+, standard forms and Twig. Supports custom builds, multiple editor configurations, asset management, and localization. Designed to be simple, predictable, and framework-native.
## Trademarks ๐
Information about CKEditorยฎ trademarks and licensing. Clarifies the relationship between this package and CKSource.
CKEditorยฎ is a trademark of [CKSource Holding sp. z o.o.](https://cksource.com/) All rights reserved. For more information about the license of CKEditorยฎ please visit [CKEditor's licensing page](https://ckeditor.com/legal/ckeditor-oss-license/).
This package is not owned by CKSource and does not use the CKEditorยฎ trademark for commercial purposes. It should not be associated with or considered an official CKSource product.
## License ๐
Details about the MIT license for this project and CKEditor 5's GPL license. Make sure to review both licenses for compliance.
This project is licensed under the terms of the [MIT LICENSE](LICENSE).
This project injects CKEditor 5 which is licensed under the terms of [GNU General Public License Version 2 or later](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html). For more information about CKEditor 5 licensing, please see their [official documentation](https://ckeditor.com/legal/ckeditor-oss-license/).