---
title: Create a custom field
description: How to create your own custom fields.
image: images/blockly_banner.png
---

import Image from '@site/src/components/Image';

# Create a custom field

Before creating a new field type, consider if one of the [other
methods](/guides/create-custom-blocks/fields/customizing-fields/overview)
for customizing fields suits your needs. If your application needs to store a
new value type, or you wish to create a new UI for an existing value type, you
probably need to create a new field type.

To create a new field, do the following:

1.  [Implement a constructor](#implementing-a-constructor).
1.  [Register a JSON key and implement `fromJson`](#json-and-registration).
1.  [Handle initialization of the on-block UI and event
    listeners](#initializing).
1.  [Handle disposal of event listeners](#disposing) (UI disposal is handled for
    you).
1.  [Implement value handling](#value-handling).
1.  [Add a text-representation of your field's value](#text).
1.  Add additional functionality such as:
    - [An editor](#creating-an-editor).
    - [On-block display updates](#updating-the-on-block-display).
    - [Serialization](#serialization).
1.  Configure additional aspects of your field, such as:
    - [Editable and serializable
      properties](#editable-and-serializable-properties)
    - [CSS](#css)
    - [Cursor](#customizing-the-cursor)

This section assumes that you have read and are familiar with the contents in
[Anatomy of a
Field](/guides/create-custom-blocks/fields/anatomy-of-a-field).

For an example of a custom field see the [Custom Fields
demo](https://raspberrypifoundation.github.io/blockly-samples/examples/pitch-field-demo/)
.

## Implementing a constructor

The field's constructor is responsible for setting up the field's initial value
and optionally setting up a [local
validator](/guides/create-custom-blocks/fields/validators). The custom
field's constructor is called during the source block initialization regardless
of whether the source block is defined in JSON or JavaScript. So, the custom
field doesn't have access to the source block during construction.

The following code sample creates a custom field named `GenericField`:

```js
class GenericField extends Blockly.Field {
  constructor(value, validator) {
    super(value, validator);

    this.SERIALIZABLE = true;
  }
}
```

### Method signature

Field constructors usually take in a value and a local validator. The value is
optional, and if you don't pass a value (or you pass a value that fails class
validation) then the default value of the superclass will be used. For the
default `Field` class, that value is `null`. If you don't want that default
value, then be sure to pass a suitable value. The validator parameter is only
present for editable fields and is typically marked as optional. Learn more
about validators in the [Validators
docs](/guides/create-custom-blocks/fields/validators).

### Structure

The logic inside of your constructor should follow this flow:

1.  Call the inherited super constructor (all custom fields should inherit from
    `Blockly.Field` or one of its subclasses) to properly initialize the value
    and set the local validator for your field.
1.  If your field is serializable, set the corresponding property in the
    constructor. Editable fields must be serializable, and fields are editable
    by default, so you should probably set this property to true unless you know
    it shouldn't be serializable.
1.  Optional: Apply additional customization (for example, [Label fields](/guides/create-custom-blocks/fields/built-in-fields/label)
    allow a css class to be passed, which is then applied to the text).

## JSON and registration

In [JSON block
definitions](/guides/create-custom-blocks/define/json-and-js), fields
are described by a string (e.g. `field_number`, `field_textinput`). Blockly
maintains a map from these strings to field objects, and calls `fromJson` on the
appropriate object during construction.

Call `Blockly.fieldRegistry.register` to add your field type to this map,
passing in the field class as the second argument:

```js
Blockly.fieldRegistry.register('field_generic', GenericField);
```

You also need to define your `fromJson` function. Your implementation should
first dereference any [references to localization
tokens](/guides/configure/translations#use-localization-tokens-in-json)
using
[`replaceMessageReferences`](/reference/blockly.utils.parsing.replacemessagereferences),
and then pass the values to the constructor.

```js
GenericField.fromJson = function (options) {
  const value = Blockly.utils.parsing.replaceMessageReferences(
    options['value'],
  );
  return new CustomFields.GenericField(value);
};
```

:::note
Local validators are not currently supported when using a JSON definition.
But they can be applied via an
[extension](/guides/create-custom-blocks/define/extensions).
:::

## Initializing

When your field is constructed, it basically only contains a value.
Initialization is where the DOM is built, the model is built (if the field
possesses a model), and events are bound.

### On-Block display

During initialization you are responsible for creating anything you will need
for the field's on-block display.

#### Defaults, background, and text

The default `initView` function creates a light coloured `rect` element and a
`text` element. If you want your field to have both of these, plus some extra
goodies, call the superclass `initView` function before adding the rest of your
DOM elements. If you want your field to have one, but not both, of these
elements you can use the `createBorderRect_` or `createTextElement_` functions.

#### Customizing DOM construction

If your field is a generic text field (e.g. [Text
Input](/guides/create-custom-blocks/fields/built-in-fields/text-input)),
DOM construction will be handled for you. Otherwise you will need to override
the `initView` function to create the DOM elements that you will need during
future rendering of your field.

For example, a dropdown field may contain both images and text. In `initView` it
creates a single image element and a single text element. Then during `render_`
it shows the active element and hides the other, based on the type of the
selected option.

Creating DOM elements can either be done using the
`Blockly.utils.dom.createSvgElement` method, or using traditional DOM creation
methods.

The requirements of a field's on-block display are:

- All DOM elements must be children of the field's `fieldGroup_`. The field
  group is created automatically.
- All DOM elements must stay inside the reported dimensions of the field.
- The root DOM element has an appropriate 
[ARIA role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Guides/Techniques).


### Accessibility

You are responsible for ensuring that your custom fields are WCAG compliant and
correctly implement `IFocusableNode`. That includes having an appropriate ARIA
role for your on-block display and making your field editor WCAG compliant.

In general, the on-block display for a clickable field should have the ARIA
role `button` to indicate that an action will take place when it is clicked or
activated.

When the on-block display is focused, its focusable DOM element will have the 
`blocklyActiveFocus` or `blocklyPassiveFocus` CSS class added. Blockly defaults
to showing a yellow outline around the focused field. You may override this
behaviour as long as you show [visible focus](https://www.w3.org/WAI/WCAG22/Understanding/focus-visible.html).


See the
[Rendering](/guides/create-custom-blocks/fields/customizing-fields/creating#updating-the-on-block-display)
section for more details on customizing and updating your on-block display.

#### Adding Text Symbols

If you want to add symbols to a field's text (such as the
[Angle](https://www.npmjs.com/package/@blockly/field-angle)
field's degree symbol) you can append the symbol element (usually contained in a
`<tspan>`) directly to the field's `textElement_`.

### Input events

By default fields register tooltip events, mousedown events, and some keyboard
events. Users can open the field 
[editor](/guides/create-custom-blocks/fields/customizing-fields/creating#creating-an-editor)
by tapping or clicking on the field, or by navigating to the field with the
keyboard and pressing `Enter`. If you want to listen for other kinds of events 
(e.g. if you want to handle dragging on a field) you should override the field's
`bindEvents_` function.

```js
bindEvents_() {
  // Call the superclass function to preserve the default behavior as well.
  super.bindEvents_();

  // Then register your own additional event listeners.
  this.mouseDownWrapper_ =
  Blockly.browserEvents.conditionalBind(this.getClickTarget_(), 'mousedown', this,
      function(event) {
        this.originalMouseX_ = event.clientX;
        this.isMouseDown_ = true;
        this.originalValue_ = this.getValue();
        event.stopPropagation();
      }
  );
  this.mouseMoveWrapper_ =
    Blockly.browserEvents.conditionalBind(document, 'mousemove', this,
      function(event) {
        if (!this.isMouseDown_) {
          return;
        }
        var delta = event.clientX - this.originalMouseX_;
        this.setValue(this.originalValue_ + delta);
      }
  );
  this.mouseUpWrapper_ =
    Blockly.browserEvents.conditionalBind(document, 'mouseup', this,
      function(_event) {
        this.isMouseDown_ = false;
      }
  );
}
```

:::note
When overriding `bindEvents_` you should always call the base function.
:::

To bind to an event you should generally use the
[`Blockly.utils.browserEvents.conditionalBind`](/reference/blockly.utils.browserevents.conditionalbind)
function. This method of binding events filters out secondary touches during
drags. If you want your handler to run even in the middle of an in-progress drag
you can use the
[`Blockly.browserEvents.bind`](/reference/blockly.utils.browserevents.bind)
function.

## Disposing

If you registered any custom event listeners inside the field's `bindEvents_`
function they will need to be unregistered inside the `dispose` function.

:::note
When overriding dispose you should always call the base function.
:::

If you correctly [initialized the
view](/guides/create-custom-blocks/fields/customizing-fields/creating#initializing)
of your field (by appending all DOM elements to the `fieldGroup_`), then the
field's DOM will be disposed of automatically.

## Value Handling

→ For information about a field's value vs its text see [Anatomy of a
field](/guides/create-custom-blocks/fields/anatomy-of-a-field).

### Validation order

<Image
  alt="Flowchart describing the order in which validators are run"
  src="/images/validation-order.svg"
  srcDark="/images/validation-order-dark.svg"
/>

### Implementing a class validator

Fields should only accept certain values. For example, number fields should only
accept numbers, colour fields should only accept colours etc. This is ensured
through class and local
[validators](/guides/create-custom-blocks/fields/validators). The class
validator follows the same rules as local validators except that it is also run
in the
[constructor](/guides/create-custom-blocks/fields/customizing-fields/creating#implementing-a-constructor)
and, as such, it should not reference the source block.

To implement your field's class validator, override the `doClassValidation_`
function.

```js
doClassValidation_(newValue) {
  if (typeof newValue != 'string') {
    return null;
  }
  return newValue;
};
```

:::note
The `newValue` passed to `doClassValidation_` could be of any type
(depending on how responsible your fellow developers are), so be prepared to
handle edge cases.
:::

### Handling valid values

If the value passed to a field with `setValue` is valid you will receive a
`doValueUpdate_` callback. By default the `doValueUpdate_` function:

- Sets the `value_` property to `newValue`.
- Sets the [`isDirty_`](/guides/create-custom-blocks/fields/customizing-fields/creating#isdirty_)
  property to `true`.

If you simply need to store the value, and don't want to do any custom handling,
you do not need to override `doValueUpdate_`.

Otherwise, if you wish to do things like:

- Custom storage of `newValue`.
- Change other properties based on `newValue`.
- Save whether the current value is valid or not.

You will need to override `doValueUpdate_`:

```js
doValueUpdate_(newValue) {
  super.doValueUpdate_(newValue);
  this.displayValue_ = newValue;
  this.isValueValid_ = true;
}
```

:::warning
If your field needs to access properties of the block or workspace when
setting the value, you should make `doValueUpdate_` fail cleanly if those are
not available.
:::

### Handling invalid values

If the value passed to the field with `setValue` is invalid you will receive a
`doValueInvalid_` callback. By default the `doValueInvalid_` function does
nothing. This means that by default invalid values will not be shown. It also
means the field will not be rerendered, because the
[`isDirty_`](/guides/create-custom-blocks/fields/customizing-fields/creating#isdirty_)
property will not be set.

If you wish to display invalid values you should override `doValueInvalid_`.
Under most circumstances you should set a `displayValue_` property to the
invalid value, set
[`isDirty_`](/guides/create-custom-blocks/fields/customizing-fields/creating#isdirty_)
to `true`, and [override
render\_](/guides/create-custom-blocks/fields/customizing-fields/creating#updating-the-on-block-display)
for the on-block display to update based on the `displayValue_` instead of the
`value_`.

```js
doValueInvalid_(newValue) {
  this.displayValue_ = newValue;
  this.isDirty_ = true;
  this.isValueValid_ = false;
}
```

:::warning
Never set the `value_` property to an invalid value. Field values
should always be valid for use in
[code generation][field-generator].
:::

### Multi-part values

When your field contains a multipart value (e.g. lists, vectors, objects) you
may wish the parts to be handled like individual values.

```js
doClassValidation_(newValue) {
  if (FieldTurtle.PATTERNS.indexOf(newValue.pattern) == -1) {
    newValue.pattern = null;
  }

  if (FieldTurtle.HATS.indexOf(newValue.hat) == -1) {
    newValue.hat = null;
  }

  if (FieldTurtle.NAMES.indexOf(newValue.turtleName) == -1) {
    newValue.turtleName = null;
  }

  if (!newValue.pattern || !newValue.hat || !newValue.turtleName) {
    this.cachedValidatedValue_ = newValue;
    return null;
  }
  return newValue;
}
```

In the above example each property of `newValue` is validated individually. Then
at the end of the `doClassValidation_` function, if any individual property is
invalid, the value is cached to the `cacheValidatedValue_` property before
returning `null` (invalid). Caching the object with individually validated
properties allows the
[`doValueInvalid_`](/guides/create-custom-blocks/fields/customizing-fields/creating#handling-invalid-values)
function to handle them separately, simply by doing a
`!this.cacheValidatedValue_.property` check, instead of re-validating each
property individually.

This pattern for validating multi-part values can also be used in [local
validators](/guides/create-custom-blocks/fields/validators) but
currently there is no way to enforce this pattern.

### isDirty\_

`isDirty_` is a flag used in the
[`setValue`](/reference/blockly.field.setvalue)
function, as well as other parts of the field, to tell if the field needs to be
re-rendered. If the field's display value has changed, `isDirty_` should usually
be set to `true`.

## Text

→ For information about where a field's text is used and how it is different
from the field's value, see [Anatomy of a
field](/guides/create-custom-blocks/fields/anatomy-of-a-field).

If the text of your field is different than the value of your field, you should
override the
[`getText`](/reference/blockly.field.gettext_)function
to provide the correct text.

```js
getText() {
  let text = this.value_.turtleName + ' wearing a ' + this.value_.hat;
  if (this.value_.hat == 'Stovepipe' || this.value_.hat == 'Propeller') {
    text += ' hat';
  }
  return text;
}
```

### ARIA value

All fields must have an ARIA type and value, which are used to construct the
[ARIA label](/guides/create-custom-blocks/fields/anatomy-of-a-field#aria-label). 
The ARIA label is generally of the form `<ARIA type>: <ARIA value>`. When
extending a built-in field you will inherit its ARIA type and value logic.
You can override the inherited ARIA logic by overriding any of `getAriaTypeName`,
`getAriaValue`, and `computeAriaLabel`.

`getAriaValue` must return a human-readable, screen reader–appropriate
representation of the field’s current value. By default, it returns the result 
of `getText`, which is not guaranteed to be informative for assistive 
technologies. You must override `getAriaValue` when `getText` might not produce
a meaningful spoken representation (for example, when possible values include 
symbols, abbreviations, or non-textual representations such as icons or colors).
If `getAriaValue` would return an empty or non-informative value, it must
instead return a localized string indicating that the value is empty.

## Creating an editor

If you define the `showEditor_` function, Blockly will automatically call
`showEditor_` and move focus into the editor when the field is activated.
You can display any HTML in your editor by wrapping it one of two special 
`div`s, called the `DropDownDiv` and `WidgetDiv`, which float above the rest of
Blockly's UI. 

You are responsible for the accessibility of your field editor, including
setting [ARIA roles and properties](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Guides/Techniques).
When possible you should use [semantic HTML](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Accessibility/HTML)
inside the `WidgetDiv` or `DropdownDiv`. 

:::info
Updates to an editor's display should be handled during
[rendering](/guides/create-custom-blocks/fields/customizing-fields/creating#updating-the-on-block-display),
instead of being handled immediately. This allows validators to intercept the
value before it is applied.
:::

### DropDownDiv vs WidgetDiv

The `DropDownDiv` is used to provide editors that live inside of a box connected
to a field. It automatically positions itself to be near the field while staying
within visible bounds. The angle picker and color picker are good examples of
the `DropDownDiv`.

![Image of angle picker](/images/angle-picker.png)

The [`WidgetDiv`](/reference/blockly.widgetdiv) is used to
provide editors that do not live inside of a box. Number fields use the
`WidgetDiv` to cover the field with an HTML text input box. While the
`DropDownDiv` handles positioning for you, the `WidgetDiv` does not. Elements
will need to be manually positioned. The coordinate system is in pixel
coordinates relative to the top left of the window. The text input editor is a
good example of the `WidgetDiv`.

<Image
  alt="Image of text input editor"
  src="/images/text-input-editor.png"
  width={200}
/>

### DropDownDiv sample code

```js
showEditor_() {
  // Create the widget HTML
  this.editor_ = this.dropdownCreate_();
  Blockly.DropDownDiv.getContentDiv().appendChild(this.editor_);

  // Set the dropdown's background colour.
  // This can be used to make it match the colour of the field.
  Blockly.DropDownDiv.setColour('white', 'silver');

  // Show it next to the field. Always pass a dispose function.
  Blockly.DropDownDiv.showPositionedByField(
      this, this.disposeWidget_.bind(this));
}
```

:::note
Creating HTML programmatically can be quite long-winded so it is not
provided here. For an example of this see the [Custom Fields
Demo](https://raspberrypifoundation.github.io/blockly-samples/examples/pitch-field-demo/)
.
:::

### WidgetDiv sample code

```js
showEditor_() {
  // Show the div. This automatically closes the dropdown if it is open.
  // Always pass a dispose function.
  Blockly.WidgetDiv.show(
    this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this));

  // Create the widget HTML.
  var widget = this.createWidget_();
  Blockly.WidgetDiv.getDiv().appendChild(widget);
}
```

:::note
Creating widget HTML programmatically can be quite long-winded so it is
not provided here. For an example of this see the implementation of [Text Input
Field](https://github.com/RaspberryPiFoundation/blockly/blob/main/packages/blockly/core/field_textinput.ts)
.
:::

:::note
If you are creating a widget that is meant to act like an input but uses a
custom element you can add `data-is-text-input='true'` to your element to notify
Blockly to treat the element as an input.
:::

### Cleaning up

Both the `DropDownDiv` and the `WidgetDiv` handle destroying the widget HTML
elements, but you need to manually dispose of any event listeners you have
applied to those elements.

```js
widgetDispose_() {
  for (let i = this.editorListeners_.length, listener;
      listener = this.editorListeners_[i]; i--) {
    Blockly.browserEvents.unbind(listener);
    this.editorListeners_.pop();
  }
}
```

The `dispose` function is called in a `null` context on the `DropDownDiv`. On
the `WidgetDiv` it is called in the context of the `WidgetDiv`. In either case
it is best to use the
[bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind)
function when passing a dispose function, as shown in the above `DropDownDiv`
and `WidgetDiv` examples.

→ For information about disposing not specific to disposing of editors see
[Disposing](/guides/create-custom-blocks/fields/customizing-fields/creating#disposing).

## Updating the on-block display

The `render_` function is used to update the field's on-block display to match
its internal value.

Common examples include:

- Change the text (dropdown)
- Change the color (color)

:::info
The field should never call `render_` directly. Instead rerenders
should be triggered by setting the
[`isDirty_`](/guides/create-custom-blocks/fields/customizing-fields/creating#isdirty_)
property to `true`.
:::

### Defaults

The default `render_` function sets the display text to the result of the
[`getDisplayText_`](/reference/blockly.field.getdisplaytext_)
function. The `getDisplayText_` function returns the field's `value_` property
cast to a string, after it has been truncated to respect the maximum text
length.

If you are using the default on-block display, and the default text behavior
works for your field, you do not need to override `render_`.

If the default text behavior works for your field, but your field's on-block
display has additional static elements, you can call the default `render_`
function, but you will still need to override it to [update the field's
size](/guides/create-custom-blocks/fields/customizing-fields/creating#updating-size).

If the default text behavior does not work for your field, or your field's
on-block display has additional dynamic elements, you will need to [customize
the `render_`
function](/guides/create-custom-blocks/fields/customizing-fields/creating#customizing-rendering).

<Image
  alt="Flowchart describing how to make decision of whether to override render_"
  src="/images/override-render-decision.svg"
  srcDark="/images/override-render-decision-dark.svg"
/>

### Customizing rendering

If the default rendering behavior does not work for your field, you will need to
define custom rendering behavior. This can involve anything from setting custom
display text, to changing image elements, to updating background colours.

All DOM attribute changes are legal, the only two things to remember are:

1.  DOM _creation_ should be handled during
    [initialization](/guides/create-custom-blocks/fields/customizing-fields/creating#initializing),
    as it is more efficient.
1.  You should always [update the `size_`](/guides/create-custom-blocks/fields/customizing-fields/creating#updating-size)
    property to match the on-block display's size.

```js
render_() {
  switch(this.value_.hat) {
    case 'Stovepipe':
      this.stovepipe_.style.display = '';
      break;
    case 'Crown':
      this.crown_.style.display = '';
      break;
    case 'Mask':
      this.mask_.style.display = '';
      break;
    case 'Propeller':
      this.propeller_.style.display = '';
      break;
    case 'Fedora':
      this.fedora_.style.display = '';
      break;
  }

  switch(this.value_.pattern) {
    case 'Dots':
      this.shellPattern_.setAttribute('fill', 'url(#polkadots)');
      break;
    case 'Stripes':
      this.shellPattern_.setAttribute('fill', 'url(#stripes)');
      break;
    case 'Hexagons':
      this.shellPattern_.setAttribute('fill', 'url(#hexagons)');
      break;
  }

  this.textContent_.nodeValue = this.value_.turtleName;

  this.updateSize_();
}
```

:::info
Always use `this.textContent_.nodeValue` to update the display text
of your field. This helps support [text
symbols](/guides/create-custom-blocks/fields/customizing-fields/creating#adding-text-symbols).
:::

### Updating size

Updating the `size_` property of a field is very important, as it informs the
block rendering code how to position the field. The best way to figure out
exactly what that `size_` should be is by experimenting.

```js
updateSize_() {
  const bbox = this.movableGroup_.getBBox();
  let width = bbox.width;
  let height = bbox.height;
  if (this.borderRect_) {
    width += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
    height += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
    this.borderRect_.setAttribute('width', width);
    this.borderRect_.setAttribute('height', height);
  }
  // Note how both the width and the height can be dynamic.
  this.size_.width = width;
  this.size_.height = height;
}
```

:::warning
Calling `getBBox` during rendering can cause layout thrashing, so you
may want to investigate other ways of getting the size.
:::

:::note
The width and height of a field can be dynamic and updating the size does
not have to be contained in its own function; it can be handled inside
`render_`. However, it is usually separated to keep code more organized.
:::

### Matching block colours

If you want elements of your field to match the colours of the block they are
attached to, you should override the `applyColour` method. You will want to
access the colour through the block's style property.

```js
applyColour() {
  const sourceBlock = this.sourceBlock_;
  if (sourceBlock.isShadow()) {
    this.arrow_.style.fill = sourceBlock.style.colourSecondary;
  } else {
    this.arrow_.style.fill = sourceBlock.style.colourPrimary;
  }
}
```

:::note
You may want to cache the colours in the `applyColour` method, so you can
apply them to your
[editor](/guides/create-custom-blocks/fields/customizing-fields/creating#creating-an-editor)
when it is activated.
:::

### Updating editability

The `updateEditable` function can be used to change how your field appears
depending on if it is editable or not. The default function makes it so the
background does/doesn't have a hover response (border) if it is/isn't editable.
The on-block display should not change size depending on its editability, but
all other changes are allowed.

```js
updateEditable() {
  if (!this.fieldGroup_) {
    // Not initialized yet.
    return;
  }
  super.updateEditable();

  const group = this.getClickTarget_();
  if (!this.isCurrentlyEditable()) {
    group.style.cursor = 'not-allowed';
  } else {
    group.style.cursor = this.CURSOR;
  }
}
```

:::warning
Messing with a field's cursor is fragile.
:::

## Serialization

[Serialization](/guides/configure/serialization) is about saving the
state of your field so that it can be reloaded into the workspace later.

The state of your workspace always includes the field's value, but it could also
include other state, such as the state of your field's UI. For example, if your
field was a zoomable map that allowed the user to select countries, you could
also serialize the zoom level.

If your field is serializable, you must set the `SERIALIZABLE` property to
`true`.

Blockly provides two sets of serialization hooks for fields. One pair of hooks
works with the new JSON serialization system, and the other pair works with the
old XML serialization system.

### `saveState` and `loadState`

`saveState` and `loadState` are serialization hooks that work with the new JSON
serialization system.

In some cases you do not need to provide these, because the default
implementations will work. If (1) your field is a direct subclass of the base
`Blockly.Field` class, (2) your value is a [JSON serializable
type](https://en.wikipedia.org/wiki/JSON#Data_types), and (3) you only need to
serialize the value, then the default implementation will work just fine!

Otherwise, your `saveState` function should return a JSON serializable
object/value which represents the state of the field. And your `loadState`
function should accept the same JSON serializable object/value, and apply it to
the field.

```js
saveState() {
  return {
    'country': this.getValue(),  // Value state
    'zoom': this.getZoomLevel(), // UI state
  };
}

loadState(state) {
  this.setValue(state['country']);
  this.setZoomLevel(state['zoom']);
}
```

#### Full serialization and backing data

`saveState` also receives an optional parameter `doFullSerialization`. This is
used by fields that normally reference state serialized by a different
[serializer][serializer] (like backing data models). The parameter signals that
the referenced state won't be available when the block is deserialized, so the
field should do all of the serialization itself. For example, this is true when
an individual block is serialized, or when a block is copy-pasted.

Two common use cases for this are:

- When an individual block is loaded into a workspace where the backing data
  model doesn't exist, the field has enough information in its own state to
  create a new data model.
- When a block is copy-pasted, the field always creates a new backing
  data model instead of referencing an existing one.

One field that uses this is the built-in variable field. Normally it serializes
the ID of the variable it is referencing, but if `doFullSerialization` is true
it serializes all of its state.

```js
saveState(doFullSerialization) {
  const state = {'id': this.variable_.getId()};
  if (doFullSerialization) {
    state['name'] = this.variable_.name;
    state['type'] = this.variable_.type;
  }
  return state;
}

loadState(state) {
  const variable = Blockly.Variables.getOrCreateVariablePackage(
      this.getSourceBlock().workspace,
      state['id'],
      state['name'],   // May not exist.
      state['type']);  // May not exist.
  this.setValue(variable.getId());
}
```

The variable field does this to make sure that if it is loaded into a workspace
where its variable doesn't exist, it can create a new variable to reference.

### `toXml` and `fromXml`

`toXml` and `fromXml` are serialization hooks that work with the old XML
serialization system. Only use these hooks if you have to (e.g. you're working
on an old codebase that hasn't migrated yet), otherwise use `saveState` and
`loadState`.

Your `toXml` function should return an XML node which represents the state of
the field. And your `fromXml` function should accept the same XML node and apply
it to the field.

```js
toXml(fieldElement) {
  fieldElement.textContent = this.getValue();
  fieldElement.setAttribute('zoom', this.getZoomLevel());
  return fieldElement;
}

fromXml(fieldElement) {
  this.setValue(fieldElement.textContent);
  this.setZoomLevel(fieldElement.getAttribute('zoom'));
}
```

## Editable and serializable properties

The `EDITABLE` property determines if the field should have UI to indicate that
it can be interacted with. It defaults to `true`.

The `SERIALIZABLE` property determines if the field should be serialized. It
defaults to `false`. If this property is `true`, you may need to provide
serialization and deserialization functions (see
[Serialization](/guides/create-custom-blocks/fields/customizing-fields/creating#serialization)).

:::info
If `EDITABLE` is `true` for your field `SERIALIZABLE` should also be
set to `true`. This is not the case by default for backwards compatibility
reasons.
:::

## Customizing with CSS \{#css\}

You can customize your field with CSS. In the [`initView`
method](#on-block-display), add a custom class to your field's `fieldGroup_`,
then reference this class in your CSS.

For example, to use a different cursor:

```js
initView() {
  ...

  // Add a custom CSS class.
  if (this.fieldGroup_) {
    Blockly.utils.dom.addClass(this.fieldGroup_, 'myCustomField');
  }
}
```

```css
.myCustomField {
  cursor: cell;
}
```

## Customizing the cursor

By default, classes that extend `FieldInput` use a `text` cursor when a user
hovers over the field, fields being dragged use a `grabbing` cursor, and all
other fields use a `default` cursor. If you want to use a different cursor, set
it [using CSS](#css).

:::note
Before version 12.0.0, the `CURSOR` property determined the cursor. Its
value was a valid CSS cursor string.
:::

[serializer]: /guides/configure/serialization#serializer-hooks
[field-generator]: /guides/create-custom-blocks/code-generation/overview#block-code-generators
