1 What is Action Text?
Action Text brings rich text content and editing to Rails. It includes the Trix editor that handles everything from formatting to links to quotes to lists to embedded images and galleries. The rich text content generated by the Trix editor is saved in its own RichText model that's associated with any existing Active Record model in the application. Any embedded images (or other attachments) are automatically stored using Active Storage and associated with the included RichText model.
2 Trix Compared to Other Rich Text Editors
Most WYSIWYG editors are wrappers around HTML’s contenteditable
and execCommand
APIs,
designed by Microsoft to support live editing of web pages in Internet Explorer 5.5,
and eventually reverse-engineered
and copied by other browsers.
Because these APIs were never fully specified or documented, and because WYSIWYG HTML editors are enormous in scope, each browser's implementation has its own set of bugs and quirks, and JavaScript developers are left to resolve the inconsistencies.
Trix sidesteps these inconsistencies by treating contenteditable
as an I/O device: when input makes its way to the editor, Trix converts that input
into an editing operation on its internal document model, then re-renders
that document back into the editor. This gives Trix complete control over what
happens after every keystroke, and avoids the need to use execCommand
at all.
3 Installation
Run bin/rails action_text:install
to add the Yarn package and copy over the necessary migration. Also, you need to set up Active Storage for embedded images and other attachments. Please refer to the Active Storage Overview guide.
Action Text uses polymorphic relationships with the action_text_rich_texts
table so that it can be shared with all models that have rich text attributes. If your models with Action Text content use UUID values for identifiers, all models that use Action Text attributes will need to use UUID values for their unique identifiers. The generated migration for Action Text will also need to be updated to specify type: :uuid
for the :record
references
line.
After the installation is complete, a Rails app should have the following changes:
Both
trix
and@rails/actiontext
should be required in your JavaScript entrypoint.// application.js import "trix" import "@rails/actiontext"
The
trix
stylesheet will be included together with Action Text styles in yourapplication.css
file.
4 Creating Rich Text Content
Add a rich text field to an existing model:
# app/models/message.rb
class Message < ApplicationRecord
has_rich_text :content
end
or add rich text field while creating a new model using:
$ bin/rails generate model Message content:rich_text
you don't need to add a content
field to your messages
table.
Then use rich_text_area
to refer to this field in the form for the model:
<%# app/views/messages/_form.html.erb %>
<%= form_with model: message do |form| %>
<div class="field">
<%= form.label :content %>
<%= form.rich_text_area :content %>
</div>
<% end %>
And finally, display the sanitized rich text on a page:
<%= @message.content %>
If there's an attached resource within content
field, it might not show properly unless you
have libvips/libvips42 package installed locally on your machine.
Check their install docs on how to get it.
To accept the rich text content, all you have to do is permit the referenced attribute:
class MessagesController < ApplicationController
def create
message = Message.create! params.require(:message).permit(:title, :content)
redirect_to message
end
end
5 Rendering Rich Text Content
By default, Action Text will render rich text content inside an element with the
.trix-content
class:
<%# app/views/layouts/action_text/contents/_content.html.erb %>
<div class="trix-content">
<%= yield %>
</div>
Elements with this class, as well as the Action Text editor, are styled by the
trix
stylesheet.
To provide your own styles instead, remove the = require trix
line from the
app/assets/stylesheets/actiontext.css
stylesheet created by the installer.
To customize the HTML rendered around rich text content, edit the
app/views/layouts/action_text/contents/_content.html.erb
layout created by the
installer.
To customize the HTML rendered for embedded images and other attachments (known
as blobs), edit the app/views/active_storage/blobs/_blob.html.erb
template
created by the installer.
5.1 Rendering attachments
In addition to attachments uploaded through Active Storage, Action Text can embed anything that can be resolved by a Signed GlobalID.
Action Text renders embedded <action-text-attachment>
elements by resolving
their sgid
attribute into an instance. Once resolved, that instance is passed
along to
render
.
The resulting HTML is embedded as a descendant of the <action-text-attachment>
element.
For example, consider a User
model:
# app/models/user.rb
class User < ApplicationRecord
has_one_attached :avatar
end
user = User.find(1)
user.to_global_id.to_s #=> gid://MyRailsApp/User/1
user.to_signed_global_id.to_s #=> BAh7CEkiCG…
Next, consider some rich text content that embeds an <action-text-attachment>
element that references the User
instance's signed GlobalID:
<p>Hello, <action-text-attachment sgid="BAh7CEkiCG…"></action-text-attachment>.</p>
Action Text resolves uses the "BAh7CEkiCG…" String to resolve the User
instance. Next, consider the application's users/user
partial:
<%# app/views/users/_user.html.erb %>
<span><%= image_tag user.avatar %> <%= user.name %></span>
The resulting HTML rendered by Action Text would look something like:
<p>Hello, <action-text-attachment sgid="BAh7CEkiCG…"><span><img src="..."> Jane Doe</span></action-text-attachment>.</p>
To render a different partial, define User#to_attachable_partial_path
:
class User < ApplicationRecord
def to_attachable_partial_path
"users/attachable"
end
end
Then declare that partial. The User
instance will be available as the user
partial-local variable:
<%# app/views/users/_attachable.html.erb %>
<span><%= image_tag user.avatar %> <%= user.name %></span>
If Action Text is unable to resolve the User
instance (for example, if the
record has been deleted), then a default fallback partial will be rendered.
Rails provides a global partial for missing attachments. This partial is installed
in your application at views/action_text/attachables/missing_attachable
and can
be modified if you want to render different HTML.
To render a different missing attachment partial, define a class-level
to_missing_attachable_partial_path
method:
class User < ApplicationRecord
def self.to_missing_attachable_partial_path
"users/missing_attachable"
end
end
Then declare that partial.
<%# app/views/users/missing_attachable.html.erb %>
<span>Deleted user</span>
To integrate with Action Text <action-text-attachment>
element rendering, a
class must:
- include the
ActionText::Attachable
module - implement
#to_sgid(**options)
(made available through theGlobalID::Identification
concern) - (optional) declare
#to_attachable_partial_path
- (optional) declare a class-level method
#to_missing_attachable_partial_path
for handling missing records
By default, all ActiveRecord::Base
descendants mix-in
GlobalID::Identification
concern, and are therefore
ActionText::Attachable
compatible.
6 Avoid N+1 Queries
If you wish to preload the dependent ActionText::RichText
model, assuming your rich text field is named content
, you can use the named scope:
Message.all.with_rich_text_content # Preload the body without attachments.
Message.all.with_rich_text_content_and_embeds # Preload both body and attachments.
7 API / Backend Development
A backend API (for example, using JSON) needs a separate endpoint for uploading files that creates an
ActiveStorage::Blob
and returns itsattachable_sgid
:{ "attachable_sgid": "BAh7CEkiCG…" }
Take that
attachable_sgid
and ask your frontend to insert it in rich text content using an<action-text-attachment>
tag:<action-text-attachment sgid="BAh7CEkiCG…"></action-text-attachment>
This is based on Basecamp, so if you still can't find what you are looking for, check this Basecamp Doc.
Feedback
You're encouraged to help improve the quality of this guide.
Please contribute if you see any typos or factual errors. To get started, you can read our documentation contributions section.
You may also find incomplete content or stuff that is not up to date. Please do add any missing documentation for main. Make sure to check Edge Guides first to verify if the issues are already fixed or not on the main branch. Check the Ruby on Rails Guides Guidelines for style and conventions.
If for whatever reason you spot something to fix but cannot patch it yourself, please open an issue.
And last but not least, any kind of discussion regarding Ruby on Rails documentation is very welcome on the official Ruby on Rails Forum.