How to build custom strapi plugin

Introduction

Strapi is a headless content management system (CMS) built with React.js and Node.js. It is easy to learn. It supports PostgreSQL, MongoDB, SQLite, MySQL and MariaDB. You can work with any of the above-mentioned databases to build your next big Strapi project.

Strapi Plugins

Strapi has a very unique yet powerful design pattern that lets you build almost anything. It has a plugin design pattern. You can think of plugins in Strapi as smaller applications with unique functionalities that are injected into the main project. It would help to think of a single Strapi plugin as a house in a housing estate. The Strapi app is the housing estate. A Strapi application can be expanded beyond the scope of its core features with the power of plugins. Remember when we talked about React.js and Node.js? You will need a background knowledge of those to be able to build upon Strapi. React and Node are the main technologies used in Strapi and have grown so much in popularity recently. You can get started looking at their respective documentations if you are not familiar with them. The scope of this tutorial would not cover the essentials of Node and React. I will assume you already know the basics

Plugin Structure

Remember how we described Strapi plugins as individual apps all within the main Strapi application. The part of the plugin that shows up on the Strapi dashboard is built with React.js components. These components are in plugin→admin→src→components. The image below further explains the folder structure of a Strapi plugin.

  plugin/
  └─── admin/ # Contains the plugin's front-end
  |     └─── src/ # Source code directory
  |          └─── index.js # Entry point of the plugin
  |          └─── pluginId.js # Name of the plugin
  |          |
  |          └─── components/ # Contains the list of React  components used by the plugin
  |          └─── containers/
  |          |    └─── App/ # Container used by every other container
  |          |    └─── Initializer/ # This container is required. It is used  to executed logic right after the plugin is mounted.
  |          └─── translations/ # Contains the translations to make the plugin internationalized
  |               └─── en.json
  |               └─── index.js # File that exports all the plugins translations.
  |               └─── fr.json
   └─── config/ # Contains the configurations of the plugin
  |     └─── functions/
  |     |    └─── bootstrap.js # Asynchronous bootstrap function that runs before the app gets started
  |     └─── policies/ # Folder containing the plugin's policies
  |     └─── queries/ # Folder containing the plugin's models queries
  |     └─── routes.json # Contains the plugin's API routes
  └─── controllers/ # Contains the plugin's API controllers
  └─── middlewares/ # Contains the plugin's middlewares
  └─── models/ # Contains the plugin's API models
  └─── services/ # Contains the plugin's API services

Install Strapi

Let's get started by installing Strapi using the following command:

    yarn create strapi-app my-plugin-project --quickstart

Using the quickstart command instructs Strapi to select all the necessary options and speeds up the installation process.

Once the installation is completed, our project will be launched in the browser prompting us to create the first admin account. Go ahead and create your admin account by providing the necessary details. Then create a few collections in the Strapi dashboard too. Look up the official documentation if you need help creating collections.

Create our plugin

To create our plugin, we will use the following Yarn command.

    yarn strapi generate:plugin customPlugin

After generating our plugin, Notice the similarity in the folder structure with what we have in the plugin structure above.

There is an index.js file at the root of the admin folder all within the plugins folder. The file contains the following:

    import pluginPkg from '../../package.json';
    import pluginId from './pluginId';
    import App from './containers/App';
    import Initializer from './containers/Initializer';
    import lifecycles from './lifecycles';
    import trads from './translations';
    export default strapi => {
      const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
      const icon = pluginPkg.strapi.icon;
      const name = pluginPkg.strapi.name;
      const plugin = {
        blockerComponent: null,
        blockerComponentProps: {},
        description: pluginDescription,
        icon,
        id: pluginId,
        initializer: Initializer,
        injectedComponents: [],
        isReady: false,
        isRequired: pluginPkg.strapi.required || false,
        layout: null,
        lifecycles,
        mainComponent: App,
        name,
        preventComponentRendering: false,
        trads,
        menu: {
          pluginsSectionLinks: [
            {
              destination: `/plugins/${pluginId}`,
              icon,
              label: {
                id: `${pluginId}.plugin.name`,
                defaultMessage: name,
              },
              name,
              permissions: [
                // Uncomment to set the permissions of the plugin here
                // {
                //   action: '', // the action name should be plugins::plugin-name.actionType
                //   subject: null,
                // },
              ],
            },
          ],
        },
      };
      return strapi.registerPlugin(plugin);
    };

Notice the pluginsSectionLinks method, it displays the icon of our plugin and mainComponent displays what we have in the App component in the component page.

The App component can be found in the containers/HomePage/index.js directory

Restart the development server with the following command:

    yarn develop --watch-admin

Edit the index.js file and add the following lines of code to it:

    const HomePage = () => {
      return (
        <div>
          <h1>I changed my title</h1>
          <p>Hi! I just earned my self a bragging right for 
              chaning the title of this plugin</p>
        </div>
      );
    };