⚠️Draft Content

Storyblok Raises $80M Series C - Read News

What’s the True Total Price of Enterprise CMS? Find out here.

Skip to main content

@storyblok/field-plugin

To bridge your field plugin application and the Visual Editor, Storyblok provides the @storyblok/field-plugin library. Storyblok also provides a command-line interface (CLI) for creating new field plugins from templates, so that you do not need to integrate @storyblok/field-plugin yourself. It's easy to get started! See the Creating Field Plugin tutorial.

This article describes the API of @storyblok/field-plugin.

Integrating @storyblok/field-plugin

To integrate @storyblok/field-plugin into a frontend framework, bootstrap your application with the --template js flag.

npx @storyblok/field-plugin-cli@latest --template js

The entry point of the application is in src/main.js. Replace this code with the following:

const rootElement = document.createElement('div')
rootElement.id = 'app'
document.body.appendChild(rootElement)

This adds a new node to the body element, to which you can mount your application.

Next, run createFieldPlugin() as an effect. Your application is now listening to events from Storyblok's Visual Editor! As an argument, this function accepts a callback function that will be called whenever the state of the field plugin changes. This happens whenever the end-user edits the content, opens a modal, selects an asset, etc. A simple example with vanilla JavaScript may look like this:

import { createFieldPlugin } from '@storyblok/field-plugin'

// This button is the only element in this application
const buttonEl = document.createElement('button')
document.body.appendChild(buttonEl)

// Establish communication with the Visual Editor
createFieldPlugin({
  onUpdateState: (response) => {
    // Handle events from the Visual Editor by re-rendering the button element
    const content = typeof response.data.content === 'number' ? response.data.content : 0
    buttonEl.textContent = `Increment: ${content}`
    buttonEl.onclick = () => response.actions.setContent(content+ 1)
  }
})

createFieldPlugin() returns a cleanup function that removes all created side effects. Call this function when your application unmounts.

API Reference

createFieldPlugin() is a function that connects field plugins to the Storyblok Visual Editor. It accepts a callback function that is invoked with a FieldPluginResponse object whenever the state of the field plugin changes.

The function returns another function that can be used to clean up the event listeners that createFieldPlugin() added.

Parameters:

  • callback: (response: FieldPluginResponse) => void: A callback function that is called whenever the state of the field plugin changes. The function is passed a FieldPluginResponse object as its only argument.

Return Value: () => void: A function that removes the event listeners added by createFieldPlugin().

FieldPluginResponse

FieldPluginResponse is an object that represents the current state of a field plugin.

Properties:

Key Defined when FieldPluginResponse.type is Description
type A string that indicates which overall state your field plugin is in. It can assume three values loading, error, and loaded.
error "error" If FieldPluginResponse.type is error, the error property will contain an Error object instance. Otherwise, it is undefined.
data "loaded" When FieldPluginResponse.type is loaded, this property will contain the state of the application that the Visual Editor has shared with the field plugin. Otherwise, it is undefined.
actions "loaded" When FieldPluginResponse.type is loaded, this property will contain an object whose properties are functions that enables the field plugin application to interact with the Visual Editor. Otherwise, it is undefined.

FieldPluginResponse.type

FieldPluginResponse.type can assume three different values:

Value Description
"loading" When the state is initially loaded and has not yet establish communication with the Visual Editor.
"error" The plugin failed to load. This can happen, for example, if the field plugin URL is opened outside the Visual Editor. This is typically not a scenario that needs to be considered.
"loaded" The plugin successfully loaded and is ready-to-use.

FieldPluginResponse.data

FieldPluginResponse.data has the following properties:

Key Description
blockUid The UID of the block that the field plugin is part of.
content The content of the field plugin that is part of the content.
isModalOpen A boolean value that indicates whether the field plugin is embedded in a modal window.
interfaceLang Specifies the language used in the Storyblok interface.
options A dictionary/record of string key-value pairs, containing the options that were set up for this field plugin in the block schema.
releaseId Represents the numerical identifier for the currently selected release. To utilize this functionality, ensure the Releases App is enabled.
releases Contains an array of objects providing details about all releases associated with the current story.
storyLang The language of the story.
spaceId The ID of the space.
story The story that was initially loaded. If the story is updated by the user, this value will remain unchanged. To update this after the initial load, call FieldPluginResponse.actions.requestContext().
storyId The ID of the story.
token A draft access token to the Content Delivery API.
translatable A boolean flag indicating whether the field plugin content is translatable.
uid The UID of the field plugin.

FieldPluginResponse.actions

FieldPluginResponse.actions has the following properties:

Key Description
setContent Updates the content of the field plugin. You can pass an object or a primitive value as an argument, and it will replace the old content. For example, setContent(3.14159).
setModalOpen Opens/Closes the modal window. For example, setModalOpen(true).
selectAsset Opens the asset selector. Returns a promise that gets resolved when the user selects an asset. For example, selectAsset().then((filename) => console.log(filename))
requestContext() Updates the request.data.story property to the version of the story that is currently opened in the Visual Editor. That is, the unsaved version of the story that exists in the user's browser memory.

Manifest File for Field Plugins

The manifest file is a configuration that enhances the functionality of your field plugin. This JSON file, named field-plugin.config.json and located in your project's root folder, is optional but highly recommended.

The manifest file allows you to configure options for your field plugin. When developing your field plugin with the Sandbox, the options are applied by default. Also, the deploy command automatically applies the options in production. So, you no longer need to configure the options manually.

Creating a Manifest File

If your field plugin doesn't already contain a manifest file, you can create one yourself at any time by following these steps:

  1. Begin by creating a field-plugin.config.json file in the root folder of your project.
  2. Populate the file with the following content:
{
  "options": []
}

That's all there is to it.

The options list within this file should consist of key-value objects representing the essential options required for your field plugin to function correctly, along with their corresponding values. This is an example of how it should be structured:

{
  "options": [
    {
      "name": "myPluginInitialValue",
      "value": 100
    }
  ]
}
Note:

The option values will not be shared when this field plugin is added to a story. Only keys are configured for security reasons.

However, during development, you'll be able to access these values while using the Field Plugin Editor or Sandbox.

Now, you just need to access these options in your code like in the example below:

const plugin = useFieldPlugin()

console.log(plugin.data.options.myPluginInitialValue)

useFieldPlugin()

You can use useFieldPlugin() for Vue 3 and React.

import { useFieldPlugin } from '@storyblok/field-plugin/vue3';
// or
import { useFieldPlugin } from '@storyblok/field-plugin/react';

const plugin = useFieldPlugin({ validateContent: ... });
console.log(plugin); // { type, data, actions }

// later
plugin.actions.setContent(...);

Using useFieldPlugin() in multiple components

You can use useFieldPlugin() in multiple components. However, in each case, the createFieldPlugin() function will be executed, transitioning each plugin.type from loading to loaded. Therefore, you must verify the plugin.type in every component. Here is an example in React (The same applies to Vue):

function Parent() {
  return (
    <div>
      <Child1 />
      <Child2 />
    </div>
  );
}

function Child1() {
  const plugin = useFieldPlugin();
  return plugin.type === "loaded" && <div>Child1: {plugin.data}</div>;
}

function Child2() {
  const plugin = useFieldPlugin();
  return plugin.type === "loaded" && <div>Child2: {plugin.data}</div>;
}

This is fine. If you want to avoid checking if the plugin is loaded repeatedly, handle it in a parent component and pass the entire plugin object to the children.

import type { FieldPluginResponse } from "@storyblok/field-plugin";
type Plugin = FieldPluginResponse<MyContentType>;

function Parent() {
  const plugin = useFieldPlugin({ validateContent: undefined });
  return (
    plugin.type === "loaded" && (
      <div>
        <Child1 plugin={plugin} />
        <Child2 plugin={plugin} />
      </div>
    )
  );
}

function Child1({ plugin: Plugin }) {
  return <div>Child1: {plugin.data}</div>;
}

function Child2({ plugin: Plugin }) {
  return <div>Child2: {plugin.data}</div>;
}