Strapi is an open-source headless CMS that enables developers to make and manage flexible API structures with ease. It comes with a great UI, and some might even say that having everything set up out of the box is one of the main upsides of Strapi.
Sometimes, however, you find yourself in a situation where you feel that everything you‘ve been given is not good enough and you wish there was some way to customise your CMS just a little more. You’ve probably searched high and low trying to find a perfect solution with no success. The good news is that I’m here to help you!
I’ll be using Strapi 3.6.6 for this tutorial — please bear in mind that the presented solution may differ in older releases. Regardless of the version, the workflow is very similar — after following this tutorial, you will be able to came up with a solution that fits your needs.
Let’s assume that I’m working on some CMS that (for some odd reasons) holds a list of fruits. My model contains two main fields — name and description.
I want to decide which fruits should be displayed on my website. That’s why I’ve created Fruit_offer— Component with one-to-many relation to Fruits.
The next step is the creation of a Single Type — Recipe. It will contain our Fruit_offer as a single component. But after going to the Recipe section… I’m not entirely satisfied with the result:
I just wish that I could pick all of the fruits at once. And having them displayed as a list with checkboxes? Oh, it sounds like a dream!
It’s time to finally make our hands dirty. This requires some copy & paste actions from node_modules installed in our Strapi project.
But don’t give up now! I promise you, it’s going to be worth it.
Open your terminal and navigate to your project directory. We have to create a copy of strapi-plugin-content’s FieldComponent and EditView directories into our project. For the first one simply type:
And for the EditView:
Before we start, we need to add one thing to our Recipe model. Find the recipe.settings.json file and create an additional property that would help us distinguish our component from the others. For me, it will be a boolean custom_ui (line 18).
Now it’s time to go back to EditView container. Find there index.js file, go to the line with fieldsBlock mapping, and create a placeholder for our new component (this way you can be sure that everything was set up correctly):
…and the result:
Are you still seeing the old version? Make sure that your app is running in watch mode. If that mode is not available for you — delete .cache&build folders and re-run your application every time you want to see the change you’ve made.
At this point, it’s good to distinguish the most important functionalities related to our component:
That’s it. Now we’re ready to start working on our component! 🎉
For this part, I’ll use components from buffetjs (already installed in strapi) to speed up the process a little bit.
If you want to get the list of all of the fruits that are available in your database, you need to make a request there. Create utils directory with fruits.js file:
Inside the components section create the FruitsComponent folder. It will contain FruitsList directory, FrutisList.parts.js with a single fruit component & index.js where you can find the logic for displaying all of the fruits.
Let’s start with FruitsList.parts.js:
Then, inside index.js in FruitsList directory:
When everything is ready, we can now create FruitsComponent (FruitsComponent/index.js). As you can see, our array of checkedFruits comes from the componentValue passed on by the connect function (lines 9, 25 & 31):
Now it’s finally time to update EditView container:
And a quick check of what our Recipe looks like in the application:
Right now, clicking on checkboxes does not change anything. But don’t worry — we’re halfway through!
The main difficulty we need to face right now is changing the state in a way that would trigger the save button (placed in the top right corner of our CMS page). In the FruitsComponent/FruitsList directory create FruitsList.hooks.js file:
Imported useContentManagerEditViewDataManager is a built-in hook that contains all available methods from EditViewDataManagerProvider. Since we are not dealing with more complicated structures (such as nested components and repeatable fields), we can use the basic ones— addRelation and onRemoveRelation. Do you remember when we created our single type — Recipe? Take a look at the screenshot below— this is where our FIELD_NAME comes from.
Now, when everything is ready, we can update FruitsList.parts.js:
Here you can find the complete code of the content-manager with topics that were not covered in this article — logic for selecting all fruits at once and displaying fruit’s description on hover.
I know that the above steps were not the easiest, but hooray, you made it! 🙌 And while we are still waiting for this feature to be released in one of the upcoming versions of Strapi, it’s good to have some alternatives for the older ones.
I highly encourage you to play with customising your fields even more— it might be a good starting point for more complex solutions and components (such as maps, colour-pickers or even custom plugins).
And don’t forget to share your final result with us on Twitter. We look forward to seeing your custom fields in action!
Last but not least, special thanks to my colleague, Kacper Łukawski, for mentoring and teaching me how to “make the Impossible Possible”.
A rich text element can be used with static or dynamic content. For static content, just drop it into any page and begin editing. For dynamic content, add a rich text field to any collection and then connect a rich text element to that field in the settings panel. Voila!
Headings, paragraphs, blockquotes, figures, images, and figure captions can all be styled after a class is added to the rich text element using the "When inside of" nested selector system.