How to replace content with widget?

I’m creating a CM6 plugin that renders HTMLBlock in HTML. When html renders, I want to hide the original HTMLBlock, and when I click the widget, I want to hide the widget and show HTMLBlock.

I tried with below code, and it’s rendering html, but the HTMLBlock is not hidden. Am I using something wrong? Thanks!

class HTMLWidget extends WidgetType {
  constructor(readonly html: string) {

  eq(other: WidgetType) {
    return (other as unknown as HTMLWidget).html === this.html;

  toDOM() {
    const div = document.createElement('div');
    div.innerHTML = this.html;
    return div;

class HTMLPlugin {
  decorations: DecorationSet;
  constructor(view: EditorView) {
    this.decorations = this.compute(view);
  update(u: ViewUpdate) {
    if (u.docChanged || u.viewportChanged || u.selectionSet) {
      this.decorations = this.compute(u.view);
  compute(view: EditorView): DecorationSet {
    const widgets: Range<Decoration>[] = [];
    for (const { from, to } of view.visibleRanges) {
      syntaxTree(view.state).iterate( {
        enter(nodeType, nodeFrom, nodeTo) {
          if ( === 'HTMLBlock') {
            const html = view.state.doc.sliceString(nodeFrom, nodeTo);
              widget: new HTMLWidget(html),
            }).range(nodeFrom, nodeTo));
    return Decoration.set(widgets, true);

You have to provide your decorations from a state field, not a view plugin, if they are able to change the vertical structure of the editor content (because the viewport depends on that structure, and view plugins update after the viewport has been computed). Maybe that’s what’s going wrong here?

Thank you for your answer. But it’s not quite understood. Can you give me a small hint with code or document?

Oh, I think you said this. I’ll give it a try. Thank you!