Skip to content
Go back

Building a Custom Strapi v5 Plugin for Admin Panel

Strapi v5 plugins use the Plugin SDK to add server routes, admin UI pages, and settings screens. Here’s the skeleton.

Prerequisites

Step 1: Create Plugin Using Plugin SDK

cd my-strapi-project
npm create strapi-plugin@latest custom-plugin

This creates plugins/custom-plugin with the proper Strapi v5 structure.

Step 2: Plugin Structure Overview

Strapi v5 plugins have a structured approach with:

Step 3: Define Server Entry Point

Edit plugins/custom-plugin/server/index.js:

"use strict";

module.exports = ({ strapi }) => {
  return {
    async initialize() {
      strapi.log.info("Custom Plugin Initialized");

      // Register routes
      strapi.server.routes([
        {
          method: "GET",
          path: "/custom/hello",
          handler: "custom-plugin.hello",
          config: { auth: false },
        },
      ]);
    },
  };
};

Step 4: Add Server Logic

Create plugins/custom-plugin/server/controllers/custom-plugin.js:

"use strict";

module.exports = {
  async hello(ctx) {
    ctx.body = {
      message: "Hello from Strapi v5 custom plugin!",
      version: "5.0.0",
    };
  },
};

Step 5: Extend Admin UI with Admin Panel API

In plugins/custom-plugin/admin/src/index.js:

import pluginPkg from "../../package.json";

export default strapi => {
  const pluginName = pluginPkg.strapi.name;

  return {
    register(app) {
      // Add menu link to admin sidebar
      app.addMenuLink({
        to: `/plugins/${pluginName}`,
        icon: "plugin",
        intlLabel: {
          id: `${pluginName}.plugin.name`,
          defaultMessage: "Custom Plugin",
        },
        Component: async () => {
          const component = await import(
            /* webpackChunkName: "custom-plugin-page" */ "./pages/App"
          );
          return component;
        },
      });

      // Add plugin settings
      app.addSettingsLink("global", {
        intlLabel: {
          id: `${pluginName}.plugin.settings`,
          defaultMessage: "Custom Plugin Settings",
        },
        id: "custom-plugin-settings",
        to: `/settings/custom-plugin`,
        Component: async () => {
          const component = await import("./pages/Settings");
          return component;
        },
      });
    },
  };
};

Create plugins/custom-plugin/admin/src/pages/App/index.js:

import React from "react";
import { request } from "@strapi/helper-plugin";
import { Main, ContentLayout, HeaderLayout } from "@strapi/design-system";

const App = () => {
  const [message, setMessage] = React.useState("");
  const [loading, setLoading] = React.useState(true);

  React.useEffect(() => {
    request("/custom/hello", { method: "GET" })
      .then(data => {
        setMessage(data.message);
        setLoading(false);
      })
      .catch(error => {
        console.error("Error fetching data:", error);
        setLoading(false);
      });
  }, []);

  return (
    <Main>
      <HeaderLayout title="Custom Plugin" subtitle="Strapi v5 Plugin Example" />
      <ContentLayout>
        {loading ? (
          <div>Loading...</div>
        ) : (
          <div>
            <h3>{message}</h3>
            <p>This is a custom plugin page in Strapi v5!</p>
          </div>
        )}
      </ContentLayout>
    </Main>
  );
};

export default App;

Step 6: Plugin Configuration

Add plugin configuration in config/plugins.js:

module.exports = {
  "custom-plugin": {
    enabled: true,
    config: {
      // Plugin-specific configuration
      apiKey: process.env.CUSTOM_PLUGIN_API_KEY,
      features: ["feature1", "feature2"],
    },
  },
};

Step 7: Build and Test

Restart Strapi to load the plugin:

npm run develop

Your plugin will appear in the admin sidebar and can be used as a local plugin or submitted to the Marketplace.

Advanced Features

Custom Fields Plugin

Plugins can also be used to add custom fields to Strapi:

module.exports = {
  type: "string",
  name: "customField",
  intlLabel: {
    id: "custom-plugin.customField",
    defaultMessage: "Custom Field",
  },
  intlDescription: {
    id: "custom-plugin.customField.description",
    defaultMessage: "A custom field for your content",
  },
};

Data Storage

Store and access data from your plugin:

module.exports = ({ strapi }) => ({
  async storeData(data) {
    return await strapi.entityService.create("plugin::custom-plugin.data", {
      data,
    });
  },

  async getData(id) {
    return await strapi.entityService.findOne("plugin::custom-plugin.data", id);
  },
});

Server API for the backend, Admin Panel API for the UI, Plugin SDK to tie it together. Works locally or on the Marketplace.


Share this post on:

Previous Post
Scaling Node.js on VPS with PM2 + Nginx
Next Post
Deploying a Fastify App with Dokploy