Introduction to Field Plugins
On May 13th, 2024, Storyblok started gradually rolling out a new design for its Visual Editor. Therefore, the Visual Editor product screenshots depicted in this resource may not match what you encounter in the Storyblok App. For more information and a detailed reference, please consult this FAQ on the new Visual Editor design.
Storyblok is built with a robust plugin system that allows you to easily create customized inputs – beyond the standard set of field types that are installed by default in every space. This allows developers to fulfill requirements that are specific to their use cases, such as integrating the Visual Editor with web-based, third-party services. These plugins are called field plugins.
For example, the Bynder asset selector allows clients to use a different asset management system than the one that is built into Storyblok:
Use the Field Plugin CLI to create new field plugins projects from templates.
Conceptual Overview
Field plugins are standalone applications that are embedded within the Visual Editor in inline frames (iframes).
API
The fact that field plugins are embedded within iframes means that they are sandboxed and do not have direct access to the Storyblok frontend application. Instead, all cross-origin communication between field plugins and the Visual Editor is handled securely via window.postMessage
. The Visual Editor can send events to the field plugin; containing such information as the current content, and the field plugin can send messages back to the Visual Editor; containing such information as the updated content.
When creating a field plugin, it is not necessary to learn exactly how this communication works as Storyblok provides a library with an abstraction layer – @storyblok/field-plugin – which exposes an object of type FieldPluginResponse with two properties:
FieldPluginResponse.data
– an object that describes the state of the field plugin. When the Visual Editor sends a message to the field plugin application, the message is parsed and stored in this object.FieldPluginResponse.actions
– an object whose properties are functions that allow the field plugin application to communicate with the Visual Editor. When invoked, @storyblok/field-plugin will send a message to the Visual Editor via the iframe window.
Serving Field Plugins
Field plugins are hosted by Storyblok, meaning that the creators of field plugins are not required to maintain any server infrastructure. The creator compiles their application into a single JavaScript bundle and uploads the content to the Storyblok application. When a Visual Editor end-user opens a block containing the field plugin, Storyblok serves a mostly empty document – except that it contains a script element with the bundle that was uploaded by the field plugin creator.
<!DOCTYPE html> <html> <head> </head> <body> <script> // Your code is inserted here </script> </body> </html>
All application logic, assets, and styles must be bundled into this one script. This has the following implications:
- CSS should be embedded within the JavaScript code because it cannot easily be linked with a link element. All field plugin starters use Vite and vite-plugin-css-injected-by-js to automatically bundle the CSS within the JavaScript bundle. This means that you can keep using CSS, Sass, CSS modules, etc. as you normally would, because the CSS bundle will be converted into JavaScript and inlined in the JavaScript bundle. Another option is to use a CSS-in-JavaScript solution such as Emotion or JSS. CSS can also be linked, but that requires you to self-host the
.css
file and dynamically insert a new link element dynamically in JavaScript. - Image assets must be Base64-encoded in the source code, as they cannot be uploaded to Storyblok via the field plugin editor. You can also self-host assets.
- Code splitting is not available, as all code must be loaded from the same endpoint.
- All code executes on the client. For server-side code, the field plugin creator is required to self-host. Storyblok recommends using serverless functions.
Features
Field plugins integrate with the Visual Editor in many ways.
Content
Field Plugins are used by editors to update the content of stories. For example, if a block schema has been configured with a field A of type custom field, the content that the field plugin produces will be stored as the value of the property A on that block.
With @storyblok/field-plugin
, use actions.setContent
to update the content model, and use data.content to read the current content.
Options
Options allow space administrators to configure field plugins without modifying the underlying code.
Inside a block schema, it is possible to pass so-called options to the field plugin. These options are provided on a per-field basis. This is useful when the field plugin needs to be configured differently across spaces or block types. For example in the Slider field plugin, different fields will require different ranges.
Inside a field plugin, read the options with actions.options
Options are key-value pairs where both the key and the value are strings. There’s no validation inside the Storyblok application, and for this reason, it is recommended that field plugin application logic validates the options at runtime and provides fallback values where the validation fails.
- Validate that the option is present and provide fallbacks where possible. For example, the slider field plugin will fall back to using the range 0-100 if the options
minValue
andmaxValue
were omitted. - Validate the format of the option. For example, the slider field plugin expects that the
minValue
andmaxValue
are stringified numbers.
Beware the kind of values you ask your space administrator users to store as options. Options are part of the content block/component schema. Thus, any party that has access to the content schema also has access to the options stored within them. If you require space administrators to provide secrets as options, they may comply and so unknowingly expose their secrets to the outside world.
Field plugins can be configured with default template options, allowing space administrators to easily identify which options are available.
For security reasons, the options values defined through the field plugin editor are accessible only during development. Once the field plugin is added to a Story, only the options keys are replicated.
Modal Dialog
Field plugins can open modal dialog windows. When a field plugin is displayed in the Visual Editor – sandwiched in-between other fields – its width is heavily limited to the content form’s width. Modal dialogs are useful when the user needs a larger surface to work in. For example, the Shopify field plugin allows the user to search and pick products from a grid in a modal dialog window so that it’s easier to find the results.
The API is different from most front-end frameworks. Because field plugins are embedded within iframes, any modal window in the field plugin application itself would be constrained by the iframe element’s bounds; an embedded application cannot open a modal window in the parent window.
To circumvent this, the Visual Editor employs a trick wherein the iframe element itself becomes a modal window. Normally, the iframe window is positioned relative to the neighboring fields. But by sending a message from the field plugin to the Visual Editor, the Visual Editor will change the position to fixed and maximize the iframe’s size. Now, the field plugin can render itself differently, based on whether it is positioned relatively or fixed in the visual editor.
With @storyblok/field-plugin
, use actions.setModalOpen
to open and close the modal window, and use data.isModalOpen
property to conditionally render based on whether the field plugin is a modal dialog.
Asset Selector
Field plugin can integrate with Storyblok’s asset manager.
Some field plugins need to access the space’s assets. For example, the annotated image field plugin allows users to annotate images with numbers which can be useful in tutorials where the article body references elements on the screen.
@storyblok/field-plugin provides a function selectAsset()
that returns a promise. When invoked, the asset selector will open on the screen, letting the user pick an asset from the asset library. When that has been done, the promise will resolve with the object that represents the asset. If the asset selector was closed, the promise will resolve with undefined
.
With @storyblok/field-plugin
, use actions.selectAsset
to open the asset selector. The returned value is a promise that will resolve the asset that the user selected.
Automatic Resizing
Field plugins vary in size, but one slight problem with iframe elements is that they cannot respond to the size of the windows that they embed.
The Visual Editor stacks the inputs for the fields on top of each other. This includes the inline frames (iframes) that field plugins are embedded within. The documents therein can have different heights, so the iframe elements need to be dynamically updated while the content editor is interacting with the field plugin. For example, the Bynder asset selector lists the selected assets vertically, and the total height depends on how many assets were selected.
Therefore, @storyblok/field-plugin listens to the height of the document. On change, a message is sent to the Visual Editor which dynamically sets the height of the wrapping iframe element in pixels.
The @storyblok/field-plugin library takes care of the automatic resizing entirely. Thus, while developing field plugins, one does not normally have to think about resizing whatsoever.
The Field Plugin Editor
To deploy a field plugin to production, you will need to upload it to Storyblok. Before you can do that, you will need to create an entity that contains the production code. This can be done in one of two ways:
- Via My Plugins – these field plugins are tied to one individual user. Only that user can manage the plugin.
- Via the Partner Portal – these field plugins are tied to an organization. Anyone in this organization can manage the plugin.
- via the Organization Portal - these field plugins are tied to an organization.
The Field Plugin Editor page consists of various several components. The large text area shows the content of the deployed bundle, i.e. the script that will be inserted into the iframe document:
The Options section contains the default options and their values (accessible only during development):
Settings & Details is where the plugin authors can assign the field plugins to spaces:
The Save button saves any changes to the field plugin configuration, and the Publish button makes the changes apply to the spaces where the field plugin is installed:
See the tutorial on how to publish a field plugin.
Field Plugin Apps
A field plugin can be managed via the field plugin editor entirely. However, this demands that the author manually assigns the field plugin to spaces. To make a field plugin easily sharable, create an extension on the partner portal of the type Field-plugin and associate it with the field-type plugin. Because the field-type plugin is now part of an extension, it can be easily installed on any space via the installation URL, and the users can manage the installation status under the Installed Apps section in the app directory.