Local plugins

Quick start

Strapi allows you to create local plugins that will work exactly the same as external ones. All your local plugins will be located in the ./plugins folder of your application.

Development Environment Setup

Create a development project

  1. Create a new project cd .. && strapi new myDevelopmentProject.
  2. cd myDevelopmentProject && strapi develop To start the Strapi project

Plugin development Setup

In a new terminal window:

Generate a new plugin: cd /path/to/myDevelopmentProject && strapi generate:plugin my-plugin

NOTE

After you have successfully generated a plugin, you need to run strapi build which adds the new plugin to the admin panel.

Plugin Folders and Files Architecture

The logic of a plugin is located at its root directory ./plugins/**. The admin panel related parts of each plugin are contained in the /admin folder. The folders and files structure are the following:

  1. plugin/
  2. └─── admin/ # Contains the plugin's front-end
  3. | └─── src/ # Source code directory
  4. | └─── index.js # Entry point of the plugin
  5. | └─── pluginId.js # Name of the plugin
  6. | |
  7. | └─── components/ # Contains the list of React components used by the plugin
  8. | └─── containers/
  9. | | └─── App/ # Container used by every others containers
  10. | | └─── Initializer/ # This container is required, it is used to executed logic right after the plugin is mounted.
  11. | └─── translations/ # Contains the translations to make the plugin internationalized
  12. | └─── en.json
  13. | └─── index.js # File that exports all the plugin's translations.
  14. | └─── fr.json
  15. └─── config/ # Contains the configurations of the plugin
  16. | └─── functions/
  17. | | └─── bootstrap.js # Asynchronous bootstrap function that runs before the app gets started
  18. | └─── policies/ # Folder containing the plugin's policies
  19. | └─── queries/ # Folder containing the plugin's models queries
  20. | └─── routes.json # Contains the plugin's API routes
  21. └─── controllers/ # Contains the plugin's API controllers
  22. └─── middlewares/ # Contains the plugin's middlewares
  23. └─── models/ # Contains the plugin's API models
  24. └─── services/ # Contains the plugin's API services

Back-end Development

This section explains how the ‘back-end part’ of your plugin works.

Routes

The plugin API routes are defined in the ./plugins/**/config/routes.json file.

TIP

Please refer to router documentation for information.

Route prefix

Each route of a plugin is prefixed by the name of the plugin (eg: /my-plugin/my-plugin-route). Using the prefix key you can change this option to something custom. You can disable the prefix, by setting the config.prefix key to an empty string.

  1. {
  2. "method": "GET",
  3. "path": "/my-plugin-route",
  4. "handler": "MyPlugin.action",
  5. "config": {
  6. "policies": [],
  7. "prefix": "my-custom-prefix"
  8. }
  9. }

CLI

The CLI can be used to generate files in the plugins folders.

Please refer to the CLI documentation for more information.

Controllers

Controllers contain functions executed according to the requested route.

Please refer to the Controllers documentation for more information.

Models

A plugin can have its own models.

Table/Collection naming

Sometimes it happens that the plugins inject models that have the same name as yours. Let’s take a quick example.

You already have User model defining in your ./api/user/models/User.settings.json API. And you decide to install the Users & Permissions plugin. This plugin also contains a User model. To avoid the conflicts, the plugins’ models are not globally exposed which means you cannot access to the plugin’s model like this:

  1. module.exports = {
  2. findUser: async function(params) {
  3. // This `User` global variable will always make a reference the User model defining in your `./api/xxx/models/User.settings.json`.
  4. return await User.find();
  5. },
  6. };

Also, the table/collection name won’t be users because you already have a User model. That’s why, the framework will automatically prefix the table/collection name for this model with the name of the plugin. Which means in our example, the table/collection name of the User model of our plugin Users & Permissions will be users-permissions_users. If you want to force the table/collection name of the plugin’s model, you can add the collectionName attribute in your model.

Please refer to the Models documentation for more information.

Policies

Global policies

A plugin can also use a globally exposed policy in the current Strapi project.

  1. {
  2. "routes": [
  3. {
  4. "method": "GET",
  5. "path": "/",
  6. "handler": "MyPlugin.index",
  7. "config": {
  8. "policies": ["global::isAuthenticated"]
  9. }
  10. }
  11. ]
  12. }

Plugin policies

A plugin can have its own policies, such as adding security rules. For instance, if the plugin includes a policy named isAuthenticated, the syntax to use this policy would be:

  1. {
  2. "routes": [
  3. {
  4. "method": "GET",
  5. "path": "/",
  6. "handler": "MyPlugin.index",
  7. "config": {
  8. "policies": ["plugins::myplugin.isAuthenticated"]
  9. }
  10. }
  11. ]
  12. }

Please refer to the Policies documentation for more information.

Front-end Development

Strapi’s admin panel and plugins system aim to be an easy and powerful way to create new features.

The admin panel is a ReactLocal plugins - 图1 (opens new window) application which can embed other React applications. These other React applications are the admin parts of each Strapi’s plugins.

Environment setup

To enable local plugin development, you need to start your application with the front-end development mode activated:

  1. $ cd my-app
  2. $ yarn develop --watch-admin
  1. $ cd my-app
  2. $ npm run develop -- --watch-admin

API

Strapi global variable

The administration exposes a global variable that is accessible for all the plugins.

strapi.backendURL

Retrieve the back-end URL. (e.g. http://localhost:1337).

strapi.currentLanguage

Retrieve the administration panel default language (e.g. en-US)

strapi.languages

Array of the administration panel’s supported languages. (e.g. ['ar', 'en', 'fr', ...]).

strapi.lockApp()

Display a loader that will prevent the user from interacting with the application.

strapi.unlockApp()

Remove the loader so the user can interact with the application

strapi.notification

Use this command anywhere in your code.

  1. strapi.notification.toggle(config);

The properties of the config object are as follows:

keytypedefaultDescription
typestringsuccesssuccess, warning or info
messageobject/stringapp.notification.successThe main message to display (works with i18n message object, { id: ‘app.notification.success’, defaultMessage: ‘Saved!’ })
titleobject/stringnullAdd a title to the notification
linkobjectnullAdd a link to the notification message { url: String, label: String|Object, target:String }
timeoutnumber2500Time in ms before the notification is closed
blockTransitionbooleanfalseBlock the notification transitions to remove the timeout
uidstringnullCustom the notification uid

The previous notification API is still working but will display a warning message in the console

  1. strapi.notification.error('app.notification.error');
  2. strapi.notification.info('app.notification.info');
  3. strapi.notification.success('app.notification.success');
  4. strapi.notification.warning('app.notification.warning');
strapi.remoteURL

The administration url (e.g. http://localhost:4000/admin).

Main plugin object

Each plugin exports all its configurations in an object. This object is located in my-plugin/admin/src/index.js

Here are its properties:

keytypeDescription
blockerComponentnodeProps can be either null or React node (e.g. () => <div />)
blockerComponentPropsobjectProps to provide to customise the blockerComponentLocal plugins - 图2 (opens new window)
descriptionstringPlugin’s description retrieved from the package.json
idstringId of the plugin from the package.json
initializernodeRefer to the Initializer documentation
injectedComponentsarrayRefer to the Injected Component documentation
isReadybooleanThe app will load until this property is true
mainComponentnodeThe plugin’s App container,
menuobjectDefine where the link of your plugin will be set. Without this your plugin will not display a link in the left menu
namestringThe plugin’s name retrieved from the package.json
pluginLogofileThe plugin’s logo
preventComponentRenderingbooleanWhether or not display the plugin’s blockerComponent instead of the main component
settingsobjectRefer to the Plugins settings API
reducersobjectThe plugin’s redux reducers
tradsobjectThe plugin’s translation files

Displaying the plugin’s link in the main menu

To display a plugin link into the main menu the plugin needs to export a menu object.

Path — plugins/my-plugin/admin/src/index.js.

  1. import pluginPkg from '../../package.json';
  2. import pluginLogo from './assets/images/logo.svg';
  3. import App from './containers/App';
  4. import lifecycles from './lifecycles';
  5. import trads from './translations';
  6. import pluginId from './pluginId';
  7. export default strapi => {
  8. const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
  9. const icon = pluginPkg.strapi.icon;
  10. const name = pluginPkg.strapi.name;
  11. const plugin = {
  12. blockerComponent: null,
  13. blockerComponentProps: {},
  14. description: pluginDescription,
  15. icon,
  16. id: pluginId,
  17. initializer: null,
  18. isRequired: pluginPkg.strapi.required || false,
  19. layout: null,
  20. lifecycles,
  21. mainComponent: App,
  22. name,
  23. pluginLogo,
  24. preventComponentRendering: false,
  25. trads,
  26. menu: {
  27. // Set a link into the PLUGINS section
  28. pluginsSectionLinks: [
  29. {
  30. destination: `/plugins/${pluginId}`, // Endpoint of the link
  31. icon,
  32. label: {
  33. id: `${pluginId}.plugin.name`, // Refers to a i18n
  34. defaultMessage: 'My PLUGIN',
  35. },
  36. name,
  37. // If the plugin has some permissions on whether or not it should be accessible
  38. // depending on the logged in user's role you can set them here.
  39. // Each permission object performs an OR comparison so if one matches the user's ones
  40. // the link will be displayed
  41. permissions: [{ action: 'plugins::content-type-builder.read', subject: null }],
  42. },
  43. ],
  44. },
  45. };
  46. return strapi.registerPlugin(plugin);
  47. };

Initializer

The component is generated by default when you create a new plugin. Use this component to execute some logic when the app is loading. When the logic has been executed this component should emit the isReady event so the user can interact with the application.

NOTE

Below is the Initializer component of the content-type-builder plugin.

It checks whether or not the auto-reload feature is enabled and depending on this value changes the mainComponent of the plugin.

  1. /**
  2. *
  3. * Initializer
  4. *
  5. */
  6. import React from 'react';
  7. import PropTypes from 'prop-types';
  8. import pluginId from '../../pluginId';
  9. class Initializer extends React.PureComponent {
  10. // eslint-disable-line react/prefer-stateless-function
  11. componentDidMount() {
  12. const {
  13. admin: { autoReload, currentEnvironment },
  14. } = this.props;
  15. let preventComponentRendering;
  16. let blockerComponentProps;
  17. if (currentEnvironment === 'production') {
  18. preventComponentRendering = true;
  19. blockerComponentProps = {
  20. blockerComponentTitle: 'components.ProductionBlocker.header',
  21. blockerComponentDescription: 'components.ProductionBlocker.description',
  22. blockerComponentIcon: 'fa-ban',
  23. blockerComponentContent: 'renderButton',
  24. };
  25. } else {
  26. // Don't render the plugin if the server autoReload is disabled
  27. preventComponentRendering = !autoReload;
  28. blockerComponentProps = {
  29. blockerComponentTitle: 'components.AutoReloadBlocker.header',
  30. blockerComponentDescription: 'components.AutoReloadBlocker.description',
  31. blockerComponentIcon: 'fa-refresh',
  32. blockerComponentContent: 'renderIde',
  33. };
  34. }
  35. // Prevent the plugin from being rendered if currentEnvironment === PRODUCTION
  36. this.props.updatePlugin(pluginId, 'preventComponentRendering', preventComponentRendering);
  37. this.props.updatePlugin(pluginId, 'blockerComponentProps', blockerComponentProps);
  38. // Emit the event plugin ready
  39. this.props.updatePlugin(pluginId, 'isReady', true);
  40. }
  41. render() {
  42. return null;
  43. }
  44. }
  45. Initializer.propTypes = {
  46. admin: PropTypes.object.isRequired,
  47. updatePlugin: PropTypes.func.isRequired,
  48. };
  49. export default Initializer;

Injected Components

(Coming soon)

Routing

The routing is based on the React Router V5Local plugins - 图3 (opens new window), due to it’s implementation each route is declared in the containers/App/index.js file.

TIP

Each route defined in a plugin must be prefixed by the plugin’s id.

Route declaration :

Let’s say that you want to create a route /user with params /:id associated with the container UserPage.

The declaration would be as follows :

Path — plugins/my-plugin/admin/src/containers/App/index.js.

  1. import React from 'react';
  2. import pluginId from '../../pluginId';
  3. import UserPage from '../UserPage';
  4. // ...
  5. class App extends React.Component {
  6. // ...
  7. render() {
  8. return (
  9. <div>
  10. <Switch>
  11. <Route exact path={`/plugins/${pluginId}/user/:id`} component={UserPage} />
  12. </Switch>
  13. </div>
  14. );
  15. }
  16. }
  17. // ...

Styling

The administration panel uses styled-componentsLocal plugins - 图4 (opens new window) for writing css.

i18n

React IntlLocal plugins - 图5 (opens new window) provides React components and an API to format dates, numbers, and strings, including pluralization and handling translations.

Usage

We recommend to set all your components text inside the translations folder.

The example below shows how to use i18n inside your plugin.

Define all your ids with the associated message:

Path — ./plugins/my-plugin/admin/src/translations/en.json.

  1. {
  2. "notification.error.message": "An error occurred"
  3. }

Path — ./plugins/my-plugin/admin/src/translations/fr.json

  1. {
  2. "notification.error.message": "Une erreur est survenue"
  3. }

Usage inside a component

Path — ./plugins/my-plugin/admin/src/components/Foo/index.js.

  1. import { FormattedMessage } from 'react-intl';
  2. import SomeOtherComponent from 'components/SomeOtherComponent';
  3. const Foo = props => (
  4. <div className={styles.foo}>
  5. <FormattedMessage id="my-plugin.notification.error.message" />
  6. <SomeOtherComponent {...props} />
  7. </div>
  8. );
  9. export default Foo;

See the documentationLocal plugins - 图6 (opens new window) for more extensive usage.

Global context

All plugins are wrapped inside the GlobalContextProvider, in this object you will have access to all plugins object as well as other utilities.

Usage:

Inside a functional component:

  1. import React from 'react';
  2. import { useGlobalContext } from 'strapi-helper-plugin';
  3. const Foo = () => {
  4. const globalContext = useGlobalContext();
  5. console.log(globalContext);
  6. return <div>Foo</div>;
  7. };

Inside a class component:

  1. import React from 'react';
  2. import { GlobalContext } from 'strapi-helper-plugin';
  3. class Foo extends React.Component {
  4. static contextType = GlobalContext;
  5. render() {
  6. console.log(this.context);
  7. return <div>Foo</div>;
  8. }
  9. }

Plugin’s front-end Field API

As plugins developer you may need to add custom fields in your application. To do so, a Field API is available in order for a plugin to register a field which will be available for all plugins.

NOTE

Currently, only the content manager uses this API to extend its current fields.

Registering a new field

Registering a field can be made in two different ways:

  1. During the load phase of a plugin
  2. Using the provided react-hook in a component.

Registering a field during the load of a plugin

Registering a field during the load phase of a plugin can be done as follows:

  1. Create a new Field type (in this example a media field type):

Path — plugins/my-plugin/admin/src/components/InputMedia/index.js.

  1. import React from 'react';
  2. const InputMedia = props => {
  3. // Check out the provided props
  4. console.log(props);
  5. return <div>InputMedia</div>;
  6. };
  7. export default InputMedia;
  1. Register the field into the application:

Path — plugins/my-plugin/admin/src/index.js.

  1. import pluginPkg from '../../package.json';
  2. import InputMedia from './components/InputMedia';
  3. import pluginId from './pluginId';
  4. export default strapi => {
  5. const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
  6. const plugin = {
  7. blockerComponent: null,
  8. blockerComponentProps: {},
  9. description: pluginDescription,
  10. icon: pluginPkg.strapi.icon,
  11. id: pluginId,
  12. initializer: () => null,
  13. injectedComponents: [],
  14. isReady: true,
  15. mainComponent: null,
  16. name: pluginPkg.strapi.name,
  17. preventComponentRendering: false,
  18. trads: {},
  19. };
  20. strapi.registerField({ type: 'media', Component: InputMedia });
  21. return strapi.registerPlugin(plugin);
  22. };

By doing so, all the plugins from your project will be able to use the newly registered Field type.

Registering a field inside a React Component

The other way to register a Field is to use the provided react-hook: useStrapi it can be done in the Initializer Component so it is accessible directly when the user is logged in, if you decide to register your plugin in another component than the Initializer the Field will only be registered in the administration panel once the component is mounted (the user has navigated to the view where the Field is registered).

  1. Register the Field in the Initializer Component:

Path — plugins/my-plugin/admin/src/containers/Initializer/index.js.

  1. /**
  2. *
  3. * Initializer
  4. *
  5. */
  6. import { useEffect, useRef } from 'react';
  7. import PropTypes from 'prop-types';
  8. import { useStrapi } from 'strapi-helper-plugin';
  9. import pluginId from '../../pluginId';
  10. import InputMedia from './components/InputMedia';
  11. const Initializer = ({ updatePlugin }) => {
  12. const {
  13. strapi: { fieldApi },
  14. } = useStrapi();
  15. const ref = useRef();
  16. ref.current = updatePlugin;
  17. useEffect(() => {
  18. // Register the new field
  19. fieldApi.registerField({ type: 'media', Component: InputMedia });
  20. ref.current(pluginId, 'isReady', true);
  21. }, []);
  22. return null;
  23. };
  24. Initializer.propTypes = {
  25. updatePlugin: PropTypes.func.isRequired,
  26. };
  27. export default Initializer;
  1. Add the Initializer component to your plugin so it is mounted in the administration panel once the user is logged in:
  1. import pluginPkg from '../../package.json';
  2. import pluginLogo from './assets/images/logo.svg';
  3. import App from './containers/App';
  4. import Initializer from './containers/Initializer';
  5. import lifecycles from './lifecycles';
  6. import trads from './translations';
  7. import pluginId from './pluginId';
  8. export default strapi => {
  9. const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
  10. const plugin = {
  11. blockerComponent: null,
  12. blockerComponentProps: {},
  13. description: pluginDescription,
  14. icon: pluginPkg.strapi.icon,
  15. id: pluginId,
  16. initializer: Initializer,
  17. injectedComponents: [],
  18. isRequired: pluginPkg.strapi.required || false,
  19. layout: null,
  20. lifecycles,
  21. mainComponent: App,
  22. name: pluginPkg.strapi.name,
  23. pluginLogo,
  24. preventComponentRendering: false,
  25. trads,
  26. };
  27. return strapi.registerPlugin(plugin);
  28. };

Consuming the Field API

Consuming the Field API can only be done by using the provided react-hook useStrapi. Here’s an example from the content-manager plugin:

Path — ~/strapi-plugin-content-manager/admin/src/components/Inputs/index.js.

  1. import React, { memo, useMemo } from 'react';
  2. // Other imports
  3. // ...
  4. // Import the Inputs component from our component library Buffet.js
  5. import { Inputs as InputsIndex } from '@buffetjs/custom';
  6. // Import the Hook with which you can access the Field API
  7. import { useStrapi } from 'strapi-helper-plugin';
  8. function Inputs({ autoFocus, keys, layout, name, onBlur }) {
  9. // This is where you will access the field API
  10. const {
  11. strapi: { fieldApi },
  12. } = useStrapi();
  13. // Other boilerplate code
  14. // ...
  15. return (
  16. <FormattedMessage id={errorId}>
  17. {error => {
  18. return (
  19. <InputsIndex
  20. {...metadatas}
  21. autoComplete="new-password"
  22. autoFocus={autoFocus}
  23. didCheckErrors={didCheckErrors}
  24. disabled={disabled}
  25. error={
  26. isEmpty(error) || errorId === temporaryErrorIdUntilBuffetjsSupportsFormattedMessage
  27. ? null
  28. : error
  29. }
  30. inputDescription={description}
  31. description={description}
  32. contentTypeUID={layout.uid}
  33. customInputs={{
  34. json: InputJSONWithErrors,
  35. wysiwyg: WysiwygWithErrors,
  36. uid: InputUID,
  37. // Retrieve all the fields that other plugins have registered
  38. ...fieldApi.getFields(),
  39. }}
  40. multiple={get(attribute, 'multiple', false)}
  41. attribute={attribute}
  42. name={keys}
  43. onBlur={onBlur}
  44. onChange={onChange}
  45. options={enumOptions}
  46. step={step}
  47. type={getInputType(type)}
  48. validations={validations}
  49. value={inputValue}
  50. withDefaultValue={false}
  51. />
  52. );
  53. }}
  54. </FormattedMessage>
  55. );
  56. }

Field API definition

MethodParamDescription
getField{String} typeRetrieve a Field depending on the type
getFieldsRetrieve all the Fields
registerField{Object}Register a Field
removeFieldRemove a Field

Plugin’s front-end settings API

As plugins developer you may need to add some settings into the main application Settings view (it corresponds to the Settings link located in the menu). To do so an API is available in order for a plugin to add links into the main view.

These settings can be declared directly into the main plugin object so they will dynamically be injected into the view.

Adding a setting

The front-end part of a plugin exports a function which registers the plugin in the administration panel. The argument is composed of two main parameters:

  • registerPlugin: Function
  • settingsBaseURL: String

Creating the links into the view’s menu

Each plugin that comes with a setting object will create a new section into the view’s menu.

The menu section can be declared as follows:

Path — plugins/my-plugin/admin/src/index.js.

  1. import pluginPkg from '../../package.json';
  2. import pluginId from './pluginId';
  3. export default strapi => {
  4. const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
  5. // Declare the links that will be injected into the settings menu
  6. const menuSection = {
  7. // Unique id of the section
  8. id: pluginId,
  9. // Title of Menu section using i18n
  10. title: {
  11. id: `${pluginId}.foo`,
  12. defaultMessage: 'Super cool setting',
  13. },
  14. // Array of links to be displayed
  15. links: [
  16. {
  17. // Using string
  18. title: 'Setting page 1',
  19. to: `${strapi.settingsBaseURL}/${pluginId}/setting1`,
  20. name: 'setting1',
  21. permissions: [{ action: 'plugins::my-plugin.action-name', subject: null }], // This key is not mandatory it can be null, undefined or an empty array
  22. },
  23. {
  24. // Using i18n with a corresponding translation key
  25. title: {
  26. id: `${pluginId}.bar`,
  27. defaultMessage: 'Setting page 2',
  28. },
  29. to: `${strapi.settingsBaseURL}/${pluginId}/setting2`,
  30. name: 'setting2',
  31. // Define a specific component if needed:
  32. Component: () => <div />,
  33. },
  34. ],
  35. };
  36. const plugin = {
  37. blockerComponent: null,
  38. blockerComponentProps: {},
  39. description: pluginDescription,
  40. icon: pluginPkg.strapi.icon,
  41. id: pluginId,
  42. initializer: () => null,
  43. injectedComponents: [],
  44. isReady: true,
  45. mainComponent: null,
  46. name: pluginPkg.strapi.name,
  47. preventComponentRendering: false,
  48. settings: {
  49. menuSection,
  50. },
  51. trads: {},
  52. };
  53. return strapi.registerPlugin(plugin);
  54. };

At this point, the plugin creates a new section (Super cool setting) which will contains two links Setting page 1 and Setting page 2 these links don’t point to any component as the corresponding one as not been declared yet.

Declaring the setting Component

The exported Setting component which receives settingsBaseURL as props in order to generate a dynamic routing which should be used to associate the two endpoints created with their corresponding components.

With the configuration from above we could easily create our plugin Settings view.

Path — plugins/my-plugin/admin/src/containers/Settings/index.js.

  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import { Switch, Route } from 'react-router-dom';
  4. import pluginId from '../../pluginId';
  5. const SettingPage1 = () => (
  6. <div>
  7. <h1>Setting Page 1</h1>
  8. </div>
  9. );
  10. const SettingPage2 = () => (
  11. <div>
  12. <h1>Setting Page 2</h1>
  13. </div>
  14. );
  15. const Settings = ({ settingsBaseURL }) => {
  16. return (
  17. <Switch>
  18. <Route component={SettingPage1} path={`${settingsBaseURL}/${pluginId}/setting1`} />
  19. <Route component={SettingPage2} path={`${settingsBaseURL}/${pluginId}/setting2`} />
  20. </Switch>
  21. );
  22. };
  23. Settings.propTypes = {
  24. settingsBaseURL: PropTypes.string.isRequired,
  25. };
  26. export default Settings;

Now that the Settings component is declared in your plugin the only thing left is to add it to your settings configuration:

Path — plugins/my-plugin/admin/src/index.js.

  1. import pluginPkg from '../../package.json';
  2. // Import the component
  3. import Settings from './containers/Settings';
  4. import pluginId from './pluginId';
  5. export default strapi => {
  6. const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
  7. // Declare the links that will be injected into the settings menu
  8. const menuSection = {
  9. id: pluginId,
  10. title: {
  11. id: `${pluginId}.foo`,
  12. defaultMessage: 'Super cool setting',
  13. },
  14. links: [
  15. {
  16. title: 'Setting page 1',
  17. to: `${strapi.settingsBaseURL}/${pluginId}/setting1`,
  18. name: 'setting1',
  19. permissions: [{ action: 'plugins::my-plugin.action-name', subject: null }],
  20. },
  21. {
  22. title: {
  23. id: `${pluginId}.bar`,
  24. defaultMessage: 'Setting page 2',
  25. },
  26. to: `${strapi.settingsBaseURL}/${pluginId}/setting2`,
  27. name: 'setting2',
  28. },
  29. ],
  30. };
  31. const plugin = {
  32. blockerComponent: null,
  33. blockerComponentProps: {},
  34. description: pluginDescription,
  35. icon: pluginPkg.strapi.icon,
  36. id: pluginId,
  37. initializer: () => null,
  38. injectedComponents: [],
  39. isReady: true,
  40. mainComponent: null,
  41. name: pluginPkg.strapi.name,
  42. preventComponentRendering: false,
  43. settings: {
  44. mainComponent: Settings,
  45. menuSection,
  46. },
  47. trads: {},
  48. };
  49. return strapi.registerPlugin(plugin);
  50. };

Adding a setting into the global section

In order to add a link into the global section of the settings view you need to create a global array containing the links you want to add:

Path — plugins/my-plugin/admin/src/index.js.

  1. import pluginPkg from '../../package.json';
  2. // Import the component
  3. import Settings from './containers/Settings';
  4. import SettingLink from './components/SettingLink';
  5. import pluginId from './pluginId';
  6. export default strapi => {
  7. const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
  8. // Declare the links that will be injected into the settings menu
  9. const menuSection = {
  10. id: pluginId,
  11. title: {
  12. id: `${pluginId}.foo`,
  13. defaultMessage: 'Super cool setting',
  14. },
  15. links: [
  16. {
  17. title: 'Setting page 1',
  18. to: `${strapi.settingsBaseURL}/${pluginId}/setting1`,
  19. name: 'setting1',
  20. },
  21. {
  22. title: {
  23. id: `${pluginId}.bar`,
  24. defaultMessage: 'Setting page 2',
  25. },
  26. to: `${strapi.settingsBaseURL}/${pluginId}/setting2`,
  27. name: 'setting2',
  28. },
  29. ],
  30. };
  31. const plugin = {
  32. blockerComponent: null,
  33. blockerComponentProps: {},
  34. description: pluginDescription,
  35. icon: pluginPkg.strapi.icon,
  36. id: pluginId,
  37. initializer: () => null,
  38. injectedComponents: [],
  39. isReady: true,
  40. mainComponent: null,
  41. name: pluginPkg.strapi.name,
  42. preventComponentRendering: false,
  43. settings: {
  44. // Add a link into the global section of the settings view
  45. global: {
  46. links: [
  47. {
  48. title: 'Setting link 1',
  49. to: `${strapi.settingsBaseURL}/setting-link-1`,
  50. name: 'settingLink1',
  51. Component: SettingLink,
  52. // Bool : https://reacttraining.com/react-router/web/api/Route/exact-bool
  53. exact: false,
  54. permissions: [{ action: 'plugins::my-plugin.action-name', subject: null }],
  55. },
  56. ],
  57. },
  58. mainComponent: Settings,
  59. menuSection,
  60. },
  61. trads: {},
  62. };
  63. return strapi.registerPlugin(plugin);
  64. };

WARNING

It is currently not possible to add a link into another plugin’s setting section