Trix
A rich text editor for everyday writing
Install / Use
/learn @basecamp/TrixREADME
Trix
A Rich Text Editor for Everyday Writing
Compose beautifully formatted text in your web application. Trix is a WYSIWYG editor for writing messages, comments, articles, and lists—the simple documents most web apps are made of. It features a sophisticated document model, support for embedded attachments, and outputs terse and consistent HTML.
Trix is an open-source project from 37signals, the creators of Ruby on Rails. Millions of people trust their text to us, and we built Trix to give them the best possible editing experience. See Trix in action in Basecamp.
Different By Design
When Trix was designed in 2014, most WYSIWYG editors were 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 not 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 sidestepped 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.
This is the approach that all modern, production ready, WYSIWYG editors now take.
Built on Web standards
<details><summary>Trix supports all evergreen, self-updating desktop and mobile browsers.</summary><img src="https://app.saucelabs.com/browser-matrix/basecamp_trix.svg"></details>Trix is built with established web standards, notably Custom Elements, Element Internals, Mutation Observer, and Promises.
Getting Started
Trix comes bundled in ESM and UMD formats and works with any asset packaging system.
The easiest way to start with Trix is including it from an npm CDN in the <head> of your page:
<head>
…
<link rel="stylesheet" type="text/css" href="https://unpkg.com/trix@2.0.8/dist/trix.css">
<script type="text/javascript" src="https://unpkg.com/trix@2.0.8/dist/trix.umd.min.js"></script>
</head>
trix.css includes default styles for the Trix toolbar, editor, and attachments. Skip this file if you prefer to define these styles yourself.
Alternatively, you can install the npm package and import it in your application:
import Trix from "trix"
document.addEventListener("trix-before-initialize", () => {
// Change Trix.config if you need
})
Creating an Editor
Place an empty <trix-editor></trix-editor> tag on the page. Trix will automatically insert a separate <trix-toolbar> before the editor.
Like an HTML <textarea>, <trix-editor> accepts autofocus and placeholder attributes. Unlike a <textarea>, <trix-editor> automatically expands vertically to fit its contents.
Creating a Toolbar
Trix automatically will create a toolbar for you and attach it right before the <trix-editor> element. If you'd like to place the toolbar in a different place you can use the toolbar attribute:
<main>
<trix-toolbar id="my_toolbar"></trix-toolbar>
<div class="more-stuff-inbetween"></div>
<trix-editor toolbar="my_toolbar" input="my_input"></trix-editor>
</main>
To change the toolbar without modifying Trix, you can overwrite the Trix.config.toolbar.getDefaultHTML() function. The default toolbar HTML is in config/toolbar.js. Trix uses data attributes to determine how to respond to a toolbar button click.
Toggle Attribute
With data-trix-attribute="<attribute name>", you can add an attribute to the current selection.
For example, to apply bold styling to the selected text the button is:
<button type="button" class="bold" data-trix-attribute="bold" data-trix-key="b"></button>
Trix will determine that a range of text is selected and will apply the formatting defined in Trix.config.textAttributes (found in config/text_attributes.js).
data-trix-key="b" tells Trix that this attribute should be applied when you use <kbd>meta</kbd>+<kbd>b</kdb>.
If the attribute is defined in Trix.config.blockAttributes, Trix will apply the attribute to the current block of text.
<button type="button" class="quote" data-trix-attribute="quote"></button>
Clicking the quote button toggles whether the block should be rendered with <blockquote>.
Integrating with Element Internals
Trix will integrate <trix-editor> elements with forms depending on the browser's support for Element Internals. If there is a need to disable support for ElementInternals, set Trix.elements.TrixEditorElement.formAssociated = false:
import Trix from "trix"
Trix.elements.TrixEditorElement.formAssociated = false
When Trix is configured to be compatible with ElementInternals, it is also
capable of functioning without an <input type="hidden"> element. To configure
a <trix-editor> element to skip creating its <input type="hidden">, set the
element's willCreateInput = false:
addEventListener("before-trix-initialize", (event) => {
const trixEditor = event.target
trixEditor.willCreateInput = false
})
[!NOTE] Trix will always use an associated
<input type="hidden">element when the[input]attribute is set. To migrate to<input>-free support, setwillCreateInput = false, then render the<trix-editor>without the[input]attribute.
[!WARNING] In the absence of an
<input type="hidden">element, the<trix-editor>element's value will not be included in<form>element submissions unless it is rendered with a[name]attribute. Set the[name]attribute to the same value that the<input type="hidden">element would have.
Invoking Internal Trix Actions
Internal actions are defined in controllers/editor_controller.js and consist of:
- undo
- redo
- link
- increaseBlockLevel
- decreaseBlockLevel
<button type="button" class="block-level decrease" data-trix-action="decreaseBlockLevel"></button>
Invoking External Custom Actions
If you want to add a button to the toolbar and have it invoke an external action, you can prefix your action name with x-. For example, if I want to print a log statement any time my new button is clicked, I would set by button's data attribute to be data-trix-action="x-log"
<button id="log-button" type="button" data-trix-action="x-log"></button>
To respond to the action, listen for trix-action-invoke. The event's target property returns a reference to the <trix-editor> element, its invokingElement property returns a reference to the <button> element, and its actionName property returns the value of the [data-trix-action] attribute. Use the value of the actionName property to detect which external action was invoked.
document.addEventListener("trix-action-invoke", function(event) {
const { target, invokingElement, actionName } = event
if (actionName === "x-log") {
console.log(`Custom ${actionName} invoked from ${invokingElement.id} button on ${target.id} trix-editor`)
}
})
Integrating With Forms
To submit the contents of a <trix-editor> with a form, first define a hidden input field in the form and assign it an id. Then reference that id in the editor’s input attribute.
<form …>
<input id="x" type="hidden" name="content">
<trix-editor input="x"></trix-editor>
</form>
Trix will automatically update the value of the hidden input field with each change to the editor.
Populating With Stored Content
To populate a <trix-editor> with stored content, include that content in the associated input element’s value attribute.
<form …>
<input id="x" value="Editor content goes here" type="hidden" name="content">
<trix-editor input="x"></trix-editor>
</form>
Use an associated input element to initially populate an editor. When an associated input element is absent, Trix will safely sanitize then load any HTML content inside a <trix-editor>…</trix-editor> tag.
<form …>
<trix-editor>Editor content goes here</trix-editor>
</form>
[!WARNING] When a
<trix-editor>element initially connects with both HTML content and an associated input element, Trix will always disregard the HTML content and load its initial content from the associated input element.
Validating the Editor
Out of the box, <trix-editor> elements support browsers' built-in [Constraint
validation][]. When rendered with the [required][] attribute, editors will be
invalid when they're completely empty. For example, consider the following HTML:
<input id="x" value="" type="hidden" name="content">
<trix-editor input="x" required></trix-editor>
Since the <trix-editor> element is [required], it is invalid when its value
is empty:
const editor = document.querySelector("trix-editor")
editor.validity.valid // => false
editor.validity.valueMissing // => true
editor.matches(":valid") // => false
editor.matches(":invalid") // => true
editor.value = "A value that isn't empty"
editor.validity.valid // => true
editor.validity.valueMissing // => false
editor.matches(":valid") // => true
editor
