Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save pedromschmitt/e36375d2024530323e32b0264b5ca08b to your computer and use it in GitHub Desktop.
Save pedromschmitt/e36375d2024530323e32b0264b5ca08b to your computer and use it in GitHub Desktop.
Integration of TinyMCE in Rails 7 with Turbo Drive Support

Integration of TinyMCE in Rails 7 with Turbo Drive Support

Since Rails 7 the turbo-rails library is included by default. Turbo Drive intercepts link clicks and form submits. It makes sure that only the <body> part of the page is rerendered instead of the whole page.

This leads to TinyMCE not being properly detached and reattached when a Turbo Drive response is rendered. The textarea will appear without the TinyMCE editor. In this post we expand on the tinymce-rails gem with a Stimulus controller to prevent this issue. The controller helps to reattach TinyMCE and respects the settings in config/tinymce.yml.

If you want to follow along or check out the end result you can find an example respository here: https://github.com/david-uhlig/example-tinymce-rails7-turbo

Instructions

1. Add the tinymce-rails gem to your Gemfile

gem 'tinymce-rails'

Then run bundle install

2. Make the assets available

# /app/views/layouts/application.html.erb
<head>
    ...
    <%= tinymce_assets %>
</head>

3. Create the tinymce.yml config file

# /config/tinymce.yml
height: 500
menubar: false
toolbar:
  - undo redo | blocks | bold italic | alignleft aligncenter alignright | bullist numlist outdent indent | removeformat | help
plugins:
  - insertdatetime lists media table code help wordcount

4. Create the Stimulus controller

// /app/javascript/controllers/tinymce_controller.js
import { Controller } from '@hotwired/stimulus'

export default class extends Controller {
    static targets = ['input']

    connect() {
        let config = Object.assign({ target: this.inputTarget }, TinyMCERails.configuration.default )
        tinymce.init(config)
    }

    disconnect () {
        if (!this.preview) tinymce.remove()
    }

    get preview () {
        return (
            document.documentElement.hasAttribute('data-turbolinks-preview') ||
            document.documentElement.hasAttribute('data-turbo-preview')
        )
    }
}

The preview() method makes sure that Turbolinks is not in cache preview mode (Source)

5. Attach the TinyMCE editor to your textarea

# /app/views/example/index.html.erb
<div data-controller="tinymce">
  <%= text_area_tag :body, "Hello, World", data: { tinymce_target: 'input' }, class: "tinymce", rows: 20, cols: 60 %>
</div>
<%= tinymce %>

We need to include the <%= tinymce %> helper method to make sure the tinymce.yml config is respected.

Attributions

Notes

Without the tinymce-rails gem

The tinymce-rails gem is not strictly necessary. You may also use the Tiny Cloud CDN similarly as described here.

0. Skip steps 1-3 and 5 from above

1. Add the following line to the <head> section of app/views/layouts/application.html.erb

<script src="https://cdn.tiny.cloud/1/no-api-key/tinymce/6/tinymce.min.js" referrerpolicy="origin"></script>

2. Add the initialization to your Stimulus controller

    initialize() {
        this.defaults = {
            selector: '.tinymce',
            toolbar: [
                'styleselect | bold italic | undo redo',
                'image | link'
            ],
            plugins: 'lists link image table code help wordcount'
        }
    }

3. Replace the connect function in the Stimulus controller

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

The settings will no longer be read from config/tinymce.yml but from the Stimulus controller instead.

4. Attach the TinyMCE editor to your textarea

# /app/views/example/index.html.erb
<div data-controller="tinymce">
  <%= text_area_tag :body, "Hello, World", data: { tinymce_target: 'input' }, class: "tinymce", rows: 20, cols: 60 %>
</div>

Note: we skip <%= tinymce %> here.

Using importmap

I would be very interested in a solution utilizing bin/importmap pin tinymce. This seems to be the favorable way going forward in Rails 7. I had no luck making it work, though.

Cheers

Hope this helps someone! Happy to hear about improvements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment