Rich Text Editors and LiveView?


I’m looking at improving the text editing capabilities in our app and would like to introduce a rich text editor experience like tiptap, trix, slate.

There doesn’t seem to be a lot of content (blogs, forum posts, etc) out there from folks who have integrated. I’m keen to hear if anyone has experience adding any of these frameworks, and looking for some recommendations for which ones are simple to add, and work well within live view?

At the moment, I’m leaning towards Trix because I’ve found a couple examples of folks using it with live view [1][2]. TipTap seems to be gaining some popularity though.


[1] WYSIWIG HTML editors for Phoenix LiveView? - #10 by romenigld
[2] Trix Editor - ElixirCasts

Well … if you are not interested in HTML editors then you could consider markdown ones. Even creating such should not be a problem. That’s just a couple of buttons that insert some text in the current selection.

The only problem with that is how LiveView treats textarea i.e. it’s always a source of truth and therefore assign does not affects it and you have to write some extra JavaScript code to workaround it which is a bit confusing:

I guess that every JavaScript editor is simple to add into LiveView as long as you understand how it works … In normal case you could simply put some JavaScript at the bottom of the body tag contents making it executable after other elements are rendered. Alternatively there are onload and other events you are interested in and you should find the information about all of it in the specific editor documentation.

The only difference here is that window load events != LiveView load. So what you should focus on are not editor-specific blog posts, but LiveView documentation like:

The only “easier” way are hex packages (if any) which does that for you, so you only have to import their JavaScript hooks and add them to your LiveView hooks.

1 Like

recently implemented a Rich Text Editor with trix, then QuillJs and then TinyMCE all of which were easy to switch between to test the functionality and ease of use. Ended up settling on tinyMCE. It all depends on what functionality you are looking for though.


There’s also another step-by-step integration for Trix which I read the other day. An extra point in favor of Trix then How to handle file upload using Trix editor in a Phoenix application


I did it with Quill once or twice. I remember it was rather simple, but the integration also was not very deep. As for Trix, I always found it lacking in terms of customization options.

I use CkEditor 5, it has great documentation and lots of features if you want.
This is my js:

Hooks.LoadEditor = {
  mounted() {
      .create(document.querySelector('#editor'), {
        toolbar: { items: ['heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'insertTable', 'undo', 'redo'] },  // 'imageUpload', 'mediaEmbed', 'insertTable', 'undo', 'redo')
        language: 'nl',
        table: { contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells'] },
        link: {
          defaultProtocol: 'http://',
          addTargetToExternalLinks: true,
          decorators: {
            addLiveviewAttrs: {
              mode: 'automatic',
              callback: url => url.startsWith('/'),
              attributes: {
                "data-phx-link": 'redirect',
                "data-phx-link-state": 'push'
      .then(editor => {
        editor.editing.view.change(writer => {
          writer.setAttribute('spellcheck', 'false', editor.editing.view.document.getRoot());
        // Add a click event listener to the editor to prevent the click event from opening the links with addLiveviewAttrs decorator
        editor.editing.view.document.on('click', (evt, data) => {
        // We need to add the text back to the textarea so changes can be tracked by LiveView,
        // otherwise when we change anything in the rest of the form, the textarea will be reset.
        editor.model.document.on('change:data', () => {
          document.querySelector('#editor').value = editor.getData();

        // Deactivate the stickyPanel, we set it manually in the css
        editor.ui.view.stickyPanel.isActive = false;
      .catch(err => {
   // This function runs when the element's parent LiveView has reconnected to the server
  reconnected() { },
  updated() {

And this is the form field, calling the Hook:

   label="Text field"