We're dsplce.co, check out our work on our website: dsplce.co π€
tiptap-rs
βοΈ Type-safe Wasm bindings for the Tiptap headless rich text editor.
tiptap-rs mirrors Tiptap's original JavaScript API as faithfully as possible, so a transition from JS feels like a transliteration rather than a rewrite β whilst handing you an idiomatic, type-safe Rust experience on top.
Disclaimer: this project has no affiliation with the official Tiptap project or trademark.
π€ Features
editor.chain().focus().toggle_bold().run()β the chained-command API is mirrored 1:1 from Tiptap's JS, so muscle memory carries over and there's nothing new to learn- Type-safe
EditorOptionsβ configure the editor with a plain Rust struct instead of poking at an untyped JS object and hoping for the best StarterKitin, batteries included β the same common-extensions bundle you'd reach for in JS, one enum variant awayCustomExtension(...)β bring your own β anything Tiptap can load, you can hand straight through; you're not boxed into what we happened to wrap- Headings as one method per level β
toggle_h1()throughtoggle_h6(), because.toggleHeading({ level })doesn't translate cleanly and we'd rather it read like Rust
Table of Contents
- π€ Features
- π¦ Installation
- π§ͺ Usage
- π API Reference
- π Examples
- π οΈ Requirements
- π Repo & Contributions
- π License
βΈ»
π¦ Installation
cargo
Add the crate to your Cargo.toml:
[]
= "0.1"
Or let cargo do it for you:
Setup
tiptap-rs binds to Tiptap running in the browser, so Tiptap itself has to be on the page. Add the following to your HTML <head> to make it available to your Wasm module:
The bindings resolve Tiptap and any extension (like StarterKit) off the global object by name β so whatever you want to use from Rust, expose it on window here first.
βΈ»
π§ͺ Usage
Create an editor
use *;
use document;
let element = document
.query_selector
.unwrap
.unwrap;
let options = EditorOptions ;
let editor = new;
Chained commands
Tiptap's original API is faithfully mirrored, so the chained commands read just like they do in JS:
// Toggle bold formatting
editor.chain.focus.toggle_bold.run;
// Toggle italic formatting
editor.chain.focus.toggle_italic.run;
// Toggle a paragraph block
editor.chain.focus.toggle_paragraph.run;
Toggling headings
Headings are the one place we deviate from the JS API on purpose: instead of .toggleHeading({ level: 1 }) you get one method per level, so it stays type-safe and reads like Rust:
editor.chain.focus.toggle_h1.run;
editor.chain.focus.toggle_h2.run;
// ... through toggle_h6()
Custom extensions
StarterKit covers the common cases, but you're not limited to it. Expose any Tiptap extension on the window in your setup script:
then hand it through with CustomExtension, which resolves it off the global object by name:
use *;
use JsValue;
use ;
let highlight = get.unwrap;
let options = EditorOptions ;
let editor = new;
Handling button events
Connect editor commands to UI buttons:
use *;
let editor_clone = editor.clone;
let callback = wrap;
let bold_button = document.query_selector.unwrap.unwrap;
bold_button
.add_event_listener_with_callback
.unwrap;
callback.forget;
βΈ»
π API Reference
Editor
The main editor instance, wrapping Tiptap's Editor class.
new
Creates a new editor instance with the specified options. Editor is Clone, so you can hand copies into event callbacks.
EditorOptions
Configuration struct for instantiating an editor:
Extension
The extension set passed to an editor:
StarterKitβ Tiptap's bundle of common extensions, resolved off the global object by name.CustomExtension(JsValue)β any other Tiptap extension you've exposed onwindow(see Custom extensions).
Both variants are re-exported from the prelude, so StarterKit and CustomExtension(...) are in scope directly.
Chained commands
Currently supported on ChainedCommands:
toggle_bold()toggle_italic()toggle_strike()toggle_paragraph()toggle_bullet_list()toggle_ordered_list()toggle_h1()throughtoggle_h6()focus()run()β execute the command chain (returnsbool)
Start a chain with editor.chain().
βΈ»
π Examples
Run the bundled example:
This requires cargo-make; it pulls in trunk on first run to build and serve the Wasm.
The example demonstrates:
- Basic editor setup
- Formatting commands wired to UI buttons
- Extension usage with
StarterKit
βΈ»
π οΈ Requirements
- A
wasm32-unknown-unknowntarget βtiptap-rsonly makes sense compiled to Wasm and run in a browser. Add it withrustup target add wasm32-unknown-unknown. - Tiptap on the page β the matching
@tiptap/*modules exposed onwindow(see Setup); the bindings call into them at runtime. - A bundler that loads it all β anything that ships your Wasm alongside the HTML works; the example uses trunk.
βΈ»
π Repo & Contributions
π¦ Crate: https://crates.io/crates/tiptap-rs π οΈ Repo: https://github.com/dsplce-co/tiptap-rs
PRs welcome π€
βΈ»
π License
MIT or Apache-2.0, at your option.