Dosu LogoDosu Logo
Ask
Join our Discord
StrapiPublic
Strapi
DocumentsStrapi
create-a-plugin
create-a-plugin
Type
External
Status
Published
Created
Mar 5, 2026
Updated
Sep 8, 2026
Updated by
Dosu Bot
Source
View

import UsingSDKplugin5 from '/docs/snippets/sdk-plugin-v5-v6.md'

Plugin creation#

The Plugin SDK generates plugins without a Strapi project and links them to an existing app with `watch:link` and yalc. In this documentation: build and verify commands to bundle the plugin for npm or marketplace publishing, and information for monorepos and local setups.

There are many ways to create a Strapi 5 plugin, but the fastest and recommended way is to use the Plugin SDK.

The Plugin SDK is a set of commands orientated around developing plugins to use them as local plugins or to publish them on NPM and/or submit them to the Marketplace.

With the Plugin SDK, you do not need to set up a Strapi project before creating a plugin.

The present guide covers creating a plugin from scratch, linking it to an existing Strapi project, and publishing the plugin. If you already have an existing plugin, you can instead retrofit the plugin setup to utilise the Plugin SDK commands (please refer to the Plugin SDK reference for a full list of available commands).

Info

This guide assumes you want to develop a plugin external to your Strapi project. However, the steps largely remain the same if you want to develop a plugin within your existing project. If you are not using a monorepo the steps are exactly the same.

must be installed globally (with npm install -g yalc or yarn global add yalc).

Getting started with the Plugin SDK#

The Plugin SDK helps you creating a plugin, linking it to an existing Strapi project, and building it for publishing.

The full list of commands and their parameters are available in the Plugin SDK reference. The present page will guide on using the main ones.

Creating the plugin#

To create your plugin, ensure you are in the parent directory of where you want it to be created and run the following command:

yarn dlx @strapi/sdk-plugin init my-strapi-plugin
npx @strapi/sdk-plugin init my-strapi-plugin

The path my-strapi-plugin can be replaced with whatever you want to call your plugin, including the path to where it should be created (e.g., code/strapi-plugins/my-new-strapi-plugin).

You will be ran through a series of prompts to help you setup your plugin. If you selected yes to all options the final structure will be similar to the default plugin structure.

Linking the plugin to your project#

In order to test your plugin during its development, the recommended approach is to link it to a Strapi project.

Linking your plugin to a project is done with the watch:link command. The command will output explanations on how to link your plugin to a Strapi project.

In a new terminal window, run the following commands:

cd /path/to/strapi/project
yarn dlx yalc add --link my-strapi-plugin && yarn install
cd /path/to/strapi/project
npx yalc add --link my-strapi-plugin && npm install
Info

In the above examples we use the name of the plugin (my-strapi-plugin) when linking it to the project. This is the name of the package, not the name of the folder.

Because this plugin is installed via node_modules you won't need to explicity add it to your plugins configuration file, so running the develop command to start your Strapi project will automatically pick up your plugin.

Now that your plugin is linked to a project, run yarn develop or npm run develop to start the Strapi application.

You are now ready to develop your plugin how you see fit! If you are making server changes, you will need to restart your server for them to take effect.

Building the plugin for publishing#

When you are ready to publish your plugin, you will need to build it. To do this, run the following command:

yarn build && yarn verify
npm run build && npm run verify

The above commands will not only build the plugin, but also verify that the output is valid and ready to be published. You can then publish your plugin to NPM as you would any other package.

:::tip Upgrading from SDK Plugin v5
If you're upgrading from @strapi/sdk-plugin v5 to v6:

  • Delete any packup.config.ts file from your plugin (it is no longer used).
  • Rely on package.json#exports for build configuration (it is now derived automatically).
  • Add --sourcemap to your build command if you need sourcemaps (they now default to off).

No other changes are required.
:::

Working with the Plugin SDK in a monorepo environment {#monorepo}#

If you are working with a monorepo environment to develop your plugin, you don't need to use the watch:link command because the monorepo workspace setup will handle the symlink. You can use the watch command instead.

However, if you are writing admin code, you might add an alias that targets the source code of your plugin to make it easier to work with within the context of the admin panel:

import path from 'node:path';

export default (config, webpack) => {
  config.resolve.alias = {
    ...config.resolve.alias,
    'my-strapi-plugin': path.resolve(
      __dirname,
      // We've assumed the plugin is local.
      '../plugins/my-strapi-plugin/admin/src'
    ),
  };

  return config;
};

Because the server looks at the server/src/index.ts|js file to import your plugin code, you must use the watch command otherwise the code will not be transpiled and the server will not be able to find your plugin.

Configuration with a local plugin#

Since the Plugin SDK is primarily designed for developing plugins, not locally, the configuration needs to be adjusted manually for local plugins.

When developing your plugin locally (using @strapi/sdk-plugin), your plugins configuration file looks like in the following example:

myplugin: {
  enabled: true,
  resolve: `./src/plugins/local-plugin`,
},

However, this setup can sometimes lead to errors such as the following:

Error: 'X must be used within StrapiApp';

This error often occurs when your plugin attempts to import core Strapi functionality, for example using:

import { unstable_useContentManagerContext as useContentManagerContext } from '@strapi/strapi/admin';

To resolve the issue, remove @strapi/strapi as a dev dependency from your plugin. This ensures that your plugin uses the same instance of Strapi's core modules as the main application, preventing conflicts and the associated errors.

Setting a local plugin in a monorepo environment without the Plugin SDK#

In a monorepo, you can configure your local plugin without using the Plugin SDK by creating 2 entry point files at the root of your plugin folder:

  • server entry point: strapi-server.js
  • admin entry point: strapi-admin.js

You create these 2 files yourself: they are not scaffolded for you. A plugin created with the Plugin SDK has no such files at its root, since the SDK declares strapi-server and strapi-admin subpath exports in the plugin's package.json instead, pointing to the built files in dist/.

Without the Plugin SDK, both entry points must be JavaScript files, even in a TypeScript project:

  • Strapi loads a local plugin's server entry point directly through Node.js, which only resolves .js and .json files. A strapi-server.ts entry point is not transpiled at load time and resolves to an empty plugin.
  • Strapi only looks for a strapi-admin.js file to detect that a local plugin has an admin part. A plugin whose admin entry point is named strapi-admin.ts is not loaded in the admin panel at all.

Files imported from strapi-admin.js can still be TypeScript, because the admin panel build compiles them.

Server entry point#

The server entry point file initializes your plugin's server-side functionalities. The expected structure for strapi-server.js is:

module.exports = () => {
  return {
    register,
    config,
    controllers,
    contentTypes,
    routes,
  };
};

Here, you export a function that returns your plugin's core components such as controllers, routes, and configuration. For more details, please refer to the Server API reference.

Admin entry point#

The admin entry point file sets up your plugin within the Strapi admin panel. The expected structure for strapi-admin.js is:

export default {
  register(app) {},
  bootstrap() {},
  registerTrads({ locales }) {},
};

This object includes methods to register your plugin with the admin application, perform bootstrapping actions, and handle translations. For more details, please refer to the Admin Panel API reference.

Tip

For a complete example of how to structure your local plugin in a monorepo environment, please check out our .

Documents
Admin Form Validation
Admin Homepage Date Serialization
Admin Modal Management
Admin Panel Locale Management
admin-panel
internationalization
setting-up-admin-panel
Auth Error Classification
AWS S3 Upload Provider
amazon-s3
media-library
Blocks Editor URL Validation
Content Manager List Configuration
Content Manager Mobile Actions Drawer
Content Manager Pagination
Content Manager Query Persistence
Content Manager RBAC
rbac
Content Manager Save State
Content Manager Status Attributes
Content Manager URL & Filter State
admin-panel-api
Content Releases
Data Transfer Streaming
Database Migration Concurrency and Idempotency
deployment
step-by-step
Date Field Serialization
Directory Export
Discard-Drafts Migration
Document ID Migration
document-service
introduction-and-faq
step-by-step
Document Service Clone & Relation Handling
Document Service Input Types
Document Service Populate Concurrency
Document Service Relation Queries
Draft & Publish Relation Synchronization
do-not-update-repeatable-components-with-document-service-api
lifecycle-hooks-document-service
publishedat-always-set-when-dandp-disabled
relations
status
status
DynamicZone Stability
components-and-dynamic-zones-do-not-return-id
components-dynamic-zones
no-shared-population-strategy-components-dynamic-zones
Enterprise License Validation
Entity String Length Validation
Error Class Identity
GraphQL Abort Signal Propagation
GraphQL Filter Pipeline
i18n Locale Validation
locale
locale
i18n Locale-Scoped Operations
crud
database-columns
document
i18n-content-manager-locale
internationalization
locale
locale
locale
no-locale-all
relations
Koa Type Augmentation
MariaDB Schema Synchronization
Media Library Asset Replace
media-library
Media Library Permissions
Media Library Thumbnails
Monorepo Module Resolution
Nested Relation Modal Navigation
OpenAPI Specification Generation
Polymorphic Relations
populate
Private Attribute Populate Validation
Query Builder Parameter Handling
Relation Modal State Management
relations
Relations Field Ordering & Pagination
Remote Asset Export
Rich Text Blocks Validation
Strapi Admin Module Loading
Strapi Attribute Typing
Strapi Content Publishing
content-manager
draft-and-publish
internationalization
parameters
releases
Strapi Data Transfer
deployment
Strapi Project Scaffolding
cli
create-a-plugin
deployment
How can I create my first Strapi project on a brand new MacBook with no prior coding experience, including all necessary terminal commands and setup steps?
quick-start
templates
Strapi TypeScript Build Performance
Strapi v5 Plugin API
admin-panel-rbac-store-updated
admin-permissions-for-plugins
breaking-changes
get-where-removed
helper-plugin
helper-plugin-deprecated
inject-content-manager-component
introduction
model-config-path-uses-uid
pass-data-from-server-to-admin
plugins-migration
rbac
redux-content-manager-app-state
remove-webhook-populate-relations
strapi-imports
Transaction Callback Promise Handling
Upload Storage Consistency
Users & Permissions OAuth Flow
new-provider-guide
users-permissions
Users-Permissions Cookie Authentication
Users-Permissions Session Management
Vite Build Configuration
vite
webpack-aliases-removed
Content Manager Mobile and Tablet UI Improvements
FilesManager API Reference
Focal Point Picker in Strapi Media Library
Nested Route File Structure for Strapi Plugins
Plugin-Specific Handling in the Strapi JavaScript Client
Strapi SDK Initialization and Architecture Guide
access-cast-environment-variables
access-configuration-values
adding-support-to-existing-project
admin-panel
admin-panel-customization
advanced-policies
advanced-queries
api
api-tokens
attributes-and-content-types-names-reserved
audit-logs
auth-zero
authentication
aws-cognito
backend-customization
bulk-operations
bundlers
cas
cli
client
cloudinary
community
configurations
configure-sso
content-api
content-history
content-manager-apis
content-type-builder
controllers
core-service-methods-use-document-service
create-components-for-plugins
cron
custom-fields
customization
data-management
database
database-identifiers-shortened
database-migrations
database-transactions
default-index-removed
default-input-validation
design-system
developing-plugins
development
discord
discord
docker
documentation
documents-and-entries
edit-view-layout-and-list-view-layout-rewritten
email
email-custom-providers
email-nodemailer
entity-service
entity-service-deprecated
environment
error-handling
examples
extension
facebook
faq
favicon
features
fetch
fields
filter
filtering
filters
filters
from-entity-service-to-document-service
functions
github
github
google
google
graphql
graphql
graphql-api-updated
guides
homepage
host-port-path
instagram
installation
installing-plugins-via-marketplace
interactive-query-builder
intro
intro
is-supported-image-removed
keycloak
keycloak
koa-body-v6
license-only
linkedin
local-upload
locales-translations
logos
mailgun-provider-variables
media-library-providers
microsoft
middlewares
middlewares
middlewares
middlewares
models
mysql5-unsupported
new-response-format
no-find-page-in-document-service
no-upload-at-entry-creation
okta
only-better-sqlite3-for-sqlite
only-mysql2-package-for-mysql
openapi
order-pagination
order-pagination
patreon
plugin-sdk
plugin-structure
plugins
plugins-extension
policies
policies
populate
populate-creator-fields
populate-select
populating
preview
project-structure
publication-state-removed
query-engine
react-router-dom-6
reddit
register-allowed-fields
removed-support-for-some-env-options
requests-responses
rest
review-workflows
routes
routes
sentry
server
server-api
server-default-log-level
server-proxy
services
services-and-controllers
setup-deployment
single-operations
sort-by-id
sort-pagination
sort-pagination
sso
store-and-access-data
strapi-container
strapi-utils-refactored
strict-requirements-config-files
templates
testing
theme-extension
twitch
twitter
typescript
typescript
understanding-populate
upgrade-to-apollov4
upgrade-tool
upgrades
upload
usage-information
use-document-id
users-and-permissions-providers
vk
webhooks
wysiwyg-editor
yarn-not-default