A state field is an [[Editor extensions|editor extension]] that lets you manage custom editor state. This page walks you through building a state field by implementing a calculator extension. The calculator should be able to add and subtract a number from the current state, and to reset the state when you want to start over. By the end of this page, you'll understand the basic concepts of building a state field. > [!note] > This page aims to distill the official CodeMirror 6 documentation for Obsidian plugin developers. For more detailed information on state fields, refer to [State Fields](https://codemirror.net/docs/guide/#state-fields). ## Prerequisites - Basic understanding of [[State management]]. ## Defining state effects State effects describe the state change you'd like to make. You may think of them as methods on a class. In the calculator example, you'd define a state effect for each of the calculator operations: ```ts const addEffect = StateEffect.define<number>(); const subtractEffect = StateEffect.define<number>(); const resetEffect = StateEffect.define(); ``` The type between the angle brackets, `<>`, defines the input type for the effect. For example, the number you want to add or subtract. The reset effect doesn't need any input, so you can leave it out. ## Defining a state field Contrary to what one might think, state fields don't actually _store_ state. They _manage_ it. State fields take the current state, applies any state effects, and returns the new state. The state field contains the calculator logic to apply the mathematical operations depending on the effects in a transaction. Since a transaction can contain multiple effects, for example two additions, the state field needs to apply them all one after another. ```ts export const calculatorField = StateField.define<number>({ create(state: EditorState): number { return 0; }, update(oldState: number, transaction: Transaction): number { let newState = oldState; for (let effect of transaction.effects) { if (effect.is(addEffect)) { newState += effect.value; } else if (effect.is(subtractEffect)) { newState -= effect.value; } else if (effect.is(resetEffect)) { newState = 0; } } return newState; }, }); ``` - `create` returns the value the calculator starts with. - `update` contains the logic for applying the effects. - `effect.is()` lets you check the type of the effect before you apply it. ## Dispatching state effects To apply a state effect to a state field, you need to dispatch it to the editor view as part of a transaction. ```ts view.dispatch({ effects: [addEffect.of(num)], }); ``` You can even define a set of helper functions that provide a more familiar API: ```ts export function add(view: EditorView, num: number) { view.dispatch({ effects: [addEffect.of(num)], }); } export function subtract(view: EditorView, num: number) { view.dispatch({ effects: [subtractEffect.of(num)], }); } export function reset(view: EditorView) { view.dispatch({ effects: [resetEffect.of(null)], }); } ``` ## Next steps Provide [[Decorations]] from your state fields to change how to display the document.