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.
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.
Trix supports all evergreen, self-updating desktop and mobile browsers.
Trix is built with established web standards, notably Custom Elements, Element Internals, Mutation Observer, and Promises.
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
})
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.
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>
<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 meta+b.
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>.
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.
Internal actions are defined in controllers/editor_controller.js and consist of:
<button type="button" class="block-level decrease" data-trix-action="decreaseBlockLevel"></button>
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`)
}
})
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.
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.
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.matches(":invalid") // => false
In addition to the built-in [required] attribute, <trix-editor>
elements support custom validation through their setCustomValidity method.
For example, consider the following HTML:
<input id="x" value="" type="hidden" name="content">
<trix-editor input="x"></trix-editor>
Custom validation can occur at any time. For example, validation can occur after
a trix-change event fired after the editor's contents change:
addEventListener("trix-change", (event) => {
const editorElement = event.target
const trixDocument = editorElement.editor.getDocument()
const isValid = (trixDocument) => {
// determine the validity based on your custom criteria
return true
}
if (isValid(trixDocument)) {
editorElement.setCustomValidity("")
} else {
editorElement.setCustomValidity("The document is not valid.")
}
}
To disable the <trix-editor>, render it with the [disabled] attribute:
<trix-editor disabled></trix-editor>
Disabled editors are not editable, cannot receive focus, and their values will
be ignored when their related <form> element is submitted.
To change whether or not an editor is disabled, either toggle the [disabled]
attribute or assign a boolean to the .disabled property:
<trix-editor id="editor" disabled></trix-editor>
<script>
const editor = document.getElementById("editor")
editor.toggleAttribute("disabled", false)
editor.disabled = true
</script>
When disabled, the editor will match the :disabled CSS pseudo-class.
Like other form controls, <trix-editor> elements should have an accessible name. The <trix-editor> element integrates with <label> elements. It supports two styles of integrating with <label> elements:
<trix-editor> element with an [id] attribute that the <label> element references through its [for] attribute:<label for="editor">Editor</label>
<trix-editor id="editor"></trix-editor>
<trix-editor> element as a child of the <label> element:<trix-toolbar id="editor-toolbar"></trix-toolbar>
<label>
Editor
<trix-editor toolbar="editor-toolbar"></trix-editor>
</label>
[!WARNING] When rendering the
<trix-editor>element as a child of the<label>element, explicitly render the corresponding<trix-toolbar>element outside of the<label>element.
In addition to integrating with <label> elements, <trix-editor> elements support [aria-label] and [aria-labelledby] attributes.
To ensure what you see when you edit is what you see when you save, use a CSS class name to scope styles for Trix formatted content. Apply this class name to your <trix-editor> element, and to a containing element when you render stored Trix content for display in your application.
<trix-editor class="trix-content"></trix-editor>
Stored content here
The default trix.css file includes styles for basic formatted content—including bulleted and numbered lists, code blocks, and block quotes—under the class name trix-content. We encourage you to use these styles as a starting point by copying them into your application’s CSS with a different class name.
Trix automatically accepts files dragged or pasted into an editor and inserts them as attachments in the document. Each attachment is considered pending until you store it remotely and provide Trix with