Skip to content

History

This chapter mainly introduces knowledge related to undo and redo plugin. By reading, you can learn about

  • How to implement undo and redo for element operations
  • How to configure whether to record additions, removals, and property changes
  • How to merge multiple changes into a single history entry

Usage

You can enable undo/redo with the History plugin, for example:

import { Graph, History } from '@antv/x6'

const graph = new Graph({
  background: {
    color: '#F2F7FA',
  },
})
graph.use(
  new History({
    enabled: true,
  }),
)

Demo

  • After moving a node freely, the Undo button becomes available.
  • Clicking the Undo button restores the node’s position, and then the Redo button becomes available.
  • Clicking the Redo button updates the node’s position.
import './index.less'
import { createRoot } from 'react-dom/client'
import React from 'react'
import { Button } from 'antd'
import { Graph, History } from '@antv/x6'

interface Props {}

interface State {
  canUndo: boolean
  canRedo: boolean
}

export default class App extends React.Component<Props, State> {
  private container: HTMLDivElement
  private graph: Graph

  state: State = {
    canRedo: false,
    canUndo: false,
  }

  componentDidMount() {
    const graph = new Graph({
      container: this.container,
      background: {
        color: '#F2F7FA',
      },
    })

    graph.use(
      new History({
        enabled: true,
      }),
    )

    graph.addNode({
      x: 100,
      y: 60,
      width: 100,
      height: 40,
      label: 'Drag Me',
      attrs: {
        body: {
          stroke: '#8f8f8f',
          strokeWidth: 1,
          fill: '#fff',
          rx: 6,
          ry: 6,
        },
      },
    })

    graph.on('history:change', () => {
      this.setState({
        canRedo: graph.canRedo(),
        canUndo: graph.canUndo(),
      })
    })

    this.graph = graph
  }

  onUndo = () => {
    this.graph.undo()
  }

  onRedo = () => {
    this.graph.redo()
  }

  refContainer = (container: HTMLDivElement) => {
    this.container = container
  }

  render() {
    return (
      <div className="history-app">
        <div className="app-btns">
          <Button.Group>
            <Button onClick={this.onUndo} disabled={!this.state.canUndo}>
              Undo
            </Button>
            <Button onClick={this.onRedo} disabled={!this.state.canRedo}>
              Redo
            </Button>
          </Button.Group>
        </div>
        <div ref={this.refContainer} className="app-content" />
      </div>
    )
  }
}

const root = createRoot(document.getElementById('container')!)
root.render(<App />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })

Configuration

Property Name Type Default Value Required Description
stackSize number 0 A stackSize of 0 means no limit; otherwise only that many entries are kept.
ignoreAdd boolean false If ignoreAdd is true, adding elements will not be recorded in the history.
ignoreRemove boolean false If ignoreRemove is true, removing elements will not be recorded in the history.
ignoreChange boolean false If ignoreChange is true, changes to element properties will not be recorded in the history.
beforeAddCommand (event, args) => any - Called before a command is added to the Undo queue; if this method returns false, the command will not be added to the Undo queue.
afterAddCommand (event, args, cmd) => any - Called after a command is added to the Undo queue.
executeCommand (cmd, revert, options) => any - Called when a command is undone or redone; revert is true if the command is undone, otherwise it indicates the command is redone.

Tip

In actual projects, we often need to undo or redo multiple changes at once. X6 provides the concept of batch, which allows multiple changes to be merged into a single history entry. Here’s how to use it:

// Method 1
graph.startBatch('custom-batch-name')
// Changing the border color of the node and modifying its position will be merged into a single record, allowing for a single undo.
node.attr('body/stroke', 'red')
node.position(30, 30)
graph.stopBatch('custom-batch-name')

// Method 2
graph.batchUpdate(() => {
  node.prop('zIndex', 10)
  node.attr('label/text', 'hello')
  node.attr('label/fill', '#ff0000')
})

API

graph.undo(…)

undo(options?: KeyValue): this

Undo. options will be passed to the event callback.

graph.undoAndCancel(…)

undoAndCancel(options?: KeyValue): this

Undo and do not add to the redo queue; the command cannot be redone. options will be passed to the event callback.

graph.redo(…)

redo(options?: KeyValue): this

Redo. options will be passed to the event callback.

graph.canUndo()

canUndo(): boolean

Returns whether undo is available.

graph.canRedo()

canRedo(): boolean

Returns whether redo is available.

graph.cleanHistory(…)

cleanHistory(options?: KeyValue): this

Clear the history queue. options will be passed to the event callback.

graph.getHistoryStackSize(…)

getHistoryStackSize(): number

Get the size of the history stack.

graph.getUndoRemainSize(…)

getUndoRemainSize(): number

Get the remaining size of the history undo stack.

graph.getUndoStackSize(…)

getUndoStackSize(): number

Get the size of the history undo stack.

graph.getRedoStackSize(…)

getRedoStackSize(): number

Get the size of the history redo stack.

graph.isHistoryEnabled()

isHistoryEnabled(): boolean

Returns whether history is enabled.

graph.enableHistory()

enableHistory(): this

Enable history state.

graph.disableHistory()

disableHistory(): this

Disable history state.

graph.toggleHistory(…)

toggleHistory(enabled?: boolean): this

Toggle the enabled state of history. Parameters are as follows:

Name Type Required Default Value Description
enabled boolean - Whether to enable history state; defaults to toggling the enabled state of history.

Events

Event Name Parameter Type Description
history:undo { cmds: Command[], options: KeyValue } Triggered when a command is undone.
history:redo { cmds: Command[], options: KeyValue } Triggered when a command is redone.
history:cancel { cmds: Command[], options: KeyValue } Triggered when a command is canceled.
history:add { cmds: Command[], options: KeyValue } Triggered when a command is added to the queue.
history:clean { cmds: Command[] | null, options: KeyValue } Triggered when the history queue is cleared.
history:change { cmds: Command[] | null, options: KeyValue } Triggered when the history queue changes.
history:batch { cmds: Command, options: KeyValue } Triggered when a batch command is received.
graph.on('history:undo', ({ cmds }) => {
  console.log(cmds)
})

// We can also listen to events on the plugin instance
history.on('undo', ({ cmds }) => {
  console.log(cmds)
})
© Copyright 2026 Ant Group Co., Ltd..备案号:京ICP备15032932号-38
Site search
Documentation and examplesType to start searching

Search documentation, APIs, and examples

↑↓Select↵OpenEscClose