Technology /
Tools
Creating a Custom Dashboard Plugin in Storyblok: A Step-by-Step Guide.
In this blog post, I'll share my experience of creating a plugin for the Storyblok CMS. We'll start with something fairly simple for our first plugin: A custom dashboard for form entries.
This project is born from a simple premise. Many of our clients want forms on their site, usually a simple contact form that just sends the message to a specific email address. This is almost always an afterthought, and it can really cause some issues in the long run. Mailing a form submission is not that hard initially, with services like SendGrid or Postmark, but the issue has to do with the edge-cases: spam folders, DNS configuration for custom mail, etc.
Inevitably, at some point, you will get an email from the client with a question like: "I tested the form and there are no messages coming through. Can you check and send all the emails I've missed so far?" With these mail services, you can keep a backup of the emails that have been sent, but this backup is not always complete, attachments can be a real issue, and they are not stored indefinitely.
So, what we do to create backups for our clients, without using third parties and adding extra hosting costs, is simple. We create a custom folder in Storyblok, something like "Form submissions". Then we create a form entry content type with all the form fields available and then we add a function in the form submission part of the website that creates a new entry in this folder for every submission. This creates a simple backup folder, but it's not very scannable and while finding specific submissions is possible with the Storyblok content search features, exporting them is not possible from within the UI.
That's where our plugin comes in. The hard work of the data collection is already done and all we need to do now is create an interface that allows you to read and export the data better than Storyblok does.
Creating a plugin
There's only one way to start making your own plugin in Storyblok: documentation.
This article helped me a lot with the initial setup https://www.storyblok.com/docs/plugins/plugin-create-a-new-plugin. I also used the NextJS starter they provide at the end of the article. https://github.com/storyblok/custom-app-examples
To actually make this plugin, we want to make it as flexible as possible. You don't want to have all the folders in the Storyblok content inside the page, only the ones containing the form entries. It is possible there will be more than one folder with form entries and there might also be different types of form entries, e.g., a contact form entry and a form entry for an open vacancy. This is just to say that we can't filter on the component types.
What we are going to do is create a settings page. This page will give you a list of all the folders in the content. There, you can select the ones containing the form entries.
When selecting a folder, it will have to be stored somewhere. This also poses a unique problem for the plugin:
The plugin has no place to store the data externally, as this would undermine the problem we are aiming to solve. Luckily, there is an option for this. When a plugin is added to a space, it adds an extra field in the settings of that space:
These settings resemble the data sources, as they consist only of names and values. While this might be limiting for many applications, it is sufficient for our specific use.
Each time a folder is selected in the settings, we save its name and id in that location. This is accomplished with the following function:
const addFolderToSettings = (folder: any) => {
fetch(
`https://mapi.storyblok.com/v1/spaces/${props.spaceId}/app_provisions/[SPECIFIC ID]/`,
{
headers: {
Authorization: process.env.STORYBLOK_PERSONAL_ACCESS,
},
},
)
.then((res) => res.json())
.then((res) => {
let fetchedData = res
fetchedData.app_provision.space_level_settings = {
...fetchedData.app_provision.space_level_settings,
[folder.name]: `${folder.id}`,
}
fetch(
`https://mapi.storyblok.com/v1/spaces/${props.spaceId}/app_provisions/[SPECIFIC ID]/`,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
Authorization: process.env.STORYBLOK_PERSONAL_ACCESS,
},
body: JSON.stringify(fetchedData),
},
)
.then((res) => res.json())
.then((res) => console.log(res))
.then(() => resetSettings())
.catch((error) => console.error(error))
})
}
The settings page will look like the example below.
The selected folders can be stored in the Storyblok space. The next step is to fetch and display all the entries in these selected folders using the Storyblok management API. This API will also be useful for adding quality of life features such as pagination and total counters in the future.
https://www.storyblok.com/docs/api/management/getting-started/introduction
https://mapi.storyblok.com/v1/spaces/${spaceId}/stories/?per_page=10&with_parent=${parentId}
With the basic setup in place, we're now going to add an export option. This will be done by converting the JSON file from the form entry into a .csv file, using the 'json-2-csv' package. While this package may not be foolproof for more complex forms—fields with components in them and asset fields may appear a bit odd—it does retain all the data. Although it might look unusual, it still provides a fairly scannable backup. Therefore, for the current use cases, we'll leave it as is.
Converting a single file will be done with the following function:
let converter = require('json-2-csv')
const exportStoryAsCSV = async () => {
const csv = await converter.json2csv(formItem?.content, {})
const a = document.createElement('a')
a.href = 'data:attachment/csv,' + encodeURIComponent(csv)
a.target = '_blank'
a.download = `${formItem.name}_export.csv`
document.body.appendChild(a)
a.click()
}
Later, we will add an 'export all' button at the top. This button will loop over all form entries, format them into a large JSON file, and convert that into a larger .csv file for easier use.
With these features, we create a basic interface that's much easier to scan than browsing through Storyblok entries.
In conclusion, creating a custom Storyblok plugin can solve specific challenges like form entry backup and data accessibility. With the right approach and utilization of the Storyblok management API, you can improve data visibility and make it easily exportable. Although this solution works best for simple forms, it's a great starting point for further customization and improvement. Overall, this project demonstrates the flexibility and power of Storyblok plugins in enhancing CMS functionality.
04 / 21 / 2023
A Step-by-Step Guide to Developing a Flexible Icon Component.
03 / 30 / 2023
How planning can help you as a (starting) designer.
10 / 26 / 2023