How to use TinyMCE with rails esbuild and stimulusjs
Tags: tinymce, rails, stimulus, esbuild
Published: 2022-09-06 14:12:39

---
title: How to use TinyMCE with rails esbuild and stimulusjs
published: true
description: 
tags: tinymce, rails, stimulusjs, esbuild
cover_image: https://camo.githubusercontent.com/893cce88ac7ccd9879ebc37d140be9017074488abdef43577f064507f78febd2/68747470733a2f2f7777772e74696e792e636c6f75642f73746f726167652f6769746875622d726561646d652d696d616765732f74696e796d63652d656469746f722e706e67
# Use a ratio of 100:42 for best results.
---

My previous post [How to use TinyMCE with rails webpacks and stimulusjs] (https://dev.to/mkhairi/how-to-use-tinymce-with-rails-webpacks-and-stimulusjs-4e4m) looked at how to integrate the full-featured text editor TinyMCE into a Rails+webpacker with copy webpack plugin feature. 

What does copy-webpack-plugin do?. It was designed to copies individual files or entire directories, which already exist, to the build directory. In this context for tinyMCE we going to copy all tinyMCE assets including plugins css/js to our build directory. so we can flexible load each plugins on demand without having to bundling all plugins assets into one compiled assets

In this article I'm going to shared on how we can setup the same approach above by using esbuild as default assets bundler

Assumed you already setup rails+esbuild+stimulusjs with [custom esbuild config](https://dev.to/bdavidxyz/custom-esbuild-for-rails-1ff5#modify-packagejson) enabled

### 1. Install TinyMCE and esbuild-plugin-copy

```sh
yarn add tinymce
yarn add esbuild-plugin-copy
```

### 2. Edit/create `esbuild.config.mjs` and configure esbuild plugin copy config 

```js
#!/usr/bin/env node

const { build } = require('esbuild')
const { copy } = require('esbuild-plugin-copy')
const path = require('path')

build({
  ....
  plugins: [
    copy({
      resolveFrom: path.join(process.cwd(), 'public/assets'),
      assets: [
        {
          from: [
            './node_modules/tinymce/**/*.js',
            './node_modules/tinymce/**/*.css'
          ],
          to: ['./tinymce'],
          keepStructure: true
        }
      ]
    })
  ]
  ...
})
  .then(() => console.log('⚡ Build complete! ⚡'))
  .catch(error => {
    console.error(error)
    process.exit(1)
  })

```
[see sample config](https://github.com/mkhairi/rails-tabler-starter/blob/main/esbuild.config.mjs)

### 3. Create a stimulus controller, tinymce_controller.js
Configure plugin definition and other tinymce option. Please refer tinyMCE documentation for more option.

```js
import tinymce from 'tinymce/tinymce'

import { Controller } from '@hotwired/stimulus'
export default class extends Controller {
  static targets = ['input']

  initialize() {
    this.defaults = {
      base_url: '/assets/tinymce',
      plugins:
        'preview importcss autolink autosave save directionality code visualblocks visualchars fullscreen image link media table charmap pagebreak nonbreaking anchor advlist lists help charmap quickbars',
      menubar: 'file edit view insert format tools table',
      toolbar:
        'undo redo | bold italic underline strikethrough | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent |  numlist bullist preview | forecolor backcolor removeformat | pagebreak | charmap | insertfile image media link anchor | ltr rtl fullscreen',
      toolbar_sticky: true,
      images_upload_url: '/image_uploader/image',
      file_picker_types: 'file image media',
      suffix: '.min',
      relative_urls: false
      //skin: (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'oxide-dark' : 'oxide'),
      //content_css: (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'default')
    }
  }

  connect() {
    // Initialize the app
    let config = Object.assign({ target: this.inputTarget }, this.defaults)
    tinymce.init(config)
  }

  disconnect() {
    tinymce.remove()
  }
}

```

### 4. Example usage in rails form

- Standard form

```erb
<%= form_with model: @object do |form| %>
  <div data-controller='tinymce'
     <%= form.text_area :intro, data: { tinymce_target: 'input' } %>
  </div>
<% end %>
```

- If you using simple form

```ruby
# create tinymce_input.rb in app/inputs
class TinymceInput < SimpleForm::Inputs::Base
  enable :placeholder, :maxlength, :minlength

  def input(wrapper_options = nil)
    options[:wrapper_html][:data] = { controller: :tinymce }
    input_html_options[:data] = { tinymce_target: 'input' }
    input_html_options[:class] = 'tinymce'
    input_html_options[:rows] ||= 20
    merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
    @builder.text_area(attribute_name, merged_input_options)
  end
end

```

```erb
<%= simple_for(@object) do |f| %>
  <%= f.input :intro,  as: :tinymce %>
<% end %>
```

[Live Demo] (https://tabler.fly.dev/pages/new)