✨ Strapi MCP is now Generally Available - let your agents manage your Strapi content ✨

How tos4 min read

How to create a Strapi v4 plugin
: File structure 2/6

May 22, 2022Updated on May 22, 2026

This article is a continuation of the following content: Generate a plugin part 1/6

When creating a plugin, this is what Strapi will generate for you in the ./src/plugins/your-plugin-folder:

├── README.md                 // You know...
├── admin                     // Front-end of your plugin.
│   └── src
│       ├── components        // Contains your front-end components.
│       │   ├── Initializer
│       │   │   └── index.js  // Plugin initializer.
│       │   └── PluginIcon
│       │       └── index.js  // Contains the icon of your plugin in the main navigation.
│       ├── index.js          // Main configurations of your plugin.
│       ├── pages             // Contains the pages of your plugin.
│       │   ├── App
│       │   │   └── index.js  // Skeleton around the actual pages.
│       │   └── HomePage
│       │       └── index.js  // Homepage of your plugin.
│       ├── pluginId.js       // pluginId variable computed from package.json name.
│       ├── translations      // Translations files to make your plugin i18n friendly
│       │   ├── en.json
│       │   └── fr.json
│       └── utils
│           ├── getTrad.js        // getTrad function to return the corresponding plugin translations
|           └── axiosInstance.js  // axios with a custom config
├── package.json
├── server                    // Back-end of your plugin
│   ├── bootstrap.js          // Function that is called right after the plugin has registered.
│   ├── config
│   │   └── index.js          // Contains the default server configuration.
│   ├── controllers
│   │   ├── index.js          // File that loads all your controllers
│   │   └── my-controller.js  // Default controller, you can rename/delete it
│   ├── destroy.js            // Function that is called to clean up the plugin after Strapi instance is destroyed
│   ├── index.js
│   ├── register.js           // Function that is called to load the plugin, before bootstrap.
│   ├── routes
│   │   └── index.js          // Plugin routes, you can update/delete it
│   └── services
│       ├── index.js          // File that loads all your services
│       └── my-service.js     // Default services, you can rename/delete it
├── strapi-admin.js           // Entrypoint for the admin (front-end)
└── strapi-server.js          // Entrypoint for the server (back-end)

A plugin is divided into two parts: admin and server. It is interesting to know that a plugin can have multiple purposes:

Server plugin

You can create a plugin that will just use the server part to enhance the API of your application. We can think of a plugin that will have its own visible or invisible content-types, controller actions, and routes that are useful for a specific use case. In such a scenario, you don't need your plugin to have a specific interface in the admin.

Admin plugin

You can create a plugin just to inject some components into the admin. However, just know that you can basically do this by creating an ./src/admin/app.js file, invoking the bootstrap lifecycle function to inject your components:

// ./src/admin/app.js
import TweetButton from './extensions/components/TweetButton'; // Component displaying a tweet button in the Content Manager

export default {
  // ...
  bootstrap(app) {
    app.injectContentManagerComponent('editView', 'right-links', {
      name: 'TweetButton',
      Component: TweetButton,
    });
  },
  // ...
};
// ./src/admin/extensions/TweetButton/index.js
import React from 'react';
import { Button } from '@strapi/design-system/Button';
import Twitter from '@strapi/icons/Twitter';
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
import { useIntl } from 'react-intl';

const TweetButton = () => {
  const { formatMessage } = useIntl();
  const { modifiedData, layout } = useCMEditViewDataManager();
  const allowedTypes = ['restaurant', 'article'];

  if (!allowedTypes.includes(layout.apiID)) {
    return <></>;
  }

  const base = layout.apiID == 'restaurant' ? 'restaurants' : 'blog';

  const handleTweet = () => {
    const tweetUrl = `https://twitter.com/intent/tweet?text=${`${encodeURIComponent(
      modifiedData.seo.metaTitle
    )} (powered by Strapi)`}&url=${process.env.STRAPI_ADMIN_CLIENT_URL}/${base}/${
      modifiedData.slug
    }`;

    window.open(tweetUrl, '_blank').focus();
  };

  const content = {
    id: 'components.TweetButton.button',
    defaultMessage: 'Share on Twitter',
  };

  return (
    <Button variant="secondary" startIcon={<Twitter />} onClick={handleTweet}>
      {formatMessage(content)}
    </Button>
  );
};

export default TweetButton;

Complete plugin

This is what we are going to do. A plugin that involves some server customization but also a nice interface in the admin. You can find a lot of plugins already on the marketplace.

Next article: Add a content-type to a plugin part 3/6

Maxime CastresGrowth Engineer

Maxime started to code in 2015 and quickly joined the Growth team of Strapi. He particularly likes to create useful content for the awesome Strapi community. Send him a meme on Twitter to make his day: @MaxCastres

How tos·12 min read

How To Build A Static Blog Using Jekyll And Strapi

Static sites deliver fast performance but present a content management challenge: how do you let writers update blog...

·August 3, 2025
Build a Blog with Astro, Strapi, and Tailwind CSS
How tos·22 min read

How to Build a Blog with Astro, Strapi, and Tailwind CSS

This article has been updated to use Astro 4 and Strapi 5 by Juliet Ofoegbu.

·October 20, 2023
Build a CRUD App with Flutter and Strapi
How tos·12 min read

How to Build a Simple CRUD Application Using Flutter & Strapi

This article has been updated to use Strapi 5 and REST API by Ekekenta Odionyefe.

·August 23, 2023