How to add classes and data to words?


Hi, newbie question here. I’m trying to tag words in a text field with IDs, and give tagged words a background-color. E.g.

The horses took off

…might result in the html:

The <span class="tagged" data-id="horse">horses</span> <span class="tagged" data-id="take_off_2">took off</span>

The learning curve is steep for ProseMirror, I’m finding it difficult finding my feet. Anyone have any tips on what PM libraries and functions might be used to add/remove data and classes to words? Not even sure if spans are the way to do it.

Many thanks


Hi there & welcome

You might want to take a look at the linter example on the website. It uses so called decorations to add classes to specific ranges in the document.

Best Frederik


Right, decorations are a good way to do this. Or, if you want these markers to actually be part of the document, use marks, which are the same mechanism that’s used for bold/emphasis/link markup.


Thanks for yr help folks. I managed to get a schema describing a mark:

  word: {
      attrs: {id: {default: ""}},
      toDOM(node) { return ["word", {id:}, 0] },
      parseDOM: [{tag: "word", getAttrs(dom) { return {id:} }}],
      inclusive: false

…which allows html like:

The <word id="horse">horses</word> <word id="take_off_2">took off</word>

Loving that inclusive functionality!

Now I’m attempting a simple test: adding a mark to a selected word using AddMarkStep:

function addTag() {
	var mark = state.schema.marks.word.create({id: "horse"});
	var step = new AddMarkStep(state.selection.from,, mark)
	var result = step.apply(state.doc);
	console.log(result.failed ? `Step failed: ${result.failed}` : `Step applied`);

When I select a word and call addTag() I get no errors but also no change to the document. Now looking for a working example of adding a mark in the guide/examples.

It took me about two hours just to find out the Mark class has a create() method which takes an attrs object as its first param. Nothing in the reference manual about it. Feels like I’m missing some basic information. Not complaining, just perplexed :confused: Any help most welcome!



The issue with your function seems to be that you’re not actually doing anything with the step that you’re creating. Look into transactions.


Thanks @marijn, my apologies. Was looking for Mark.create() for some reason.

I followed your advice and cobbled together some code from random bits of the guide and manual. It works!

function addTag() {
	let tr =
	let mark = state.schema.marks.word.create({id: "horse"});
	let step = new AddMarkStep(state.selection.from,, mark)

@marijn in your opinion, would this be a healthy way to go about performing a transaction? For example, I’m not sure why step.apply() isn’t needed (and in what situation it would be).

Again, thanks!


The helper method addMark could shorten this a bit more, but other than that it looks good.

Step.apply is a primitive for when you are dealing directly with steps (for example when replaying history), you rarely need to use it directly.