If you’ve ever tried to integrate a local plugin into your Strapi v5 project, you might have felt the frustration. You want to add a custom widget or extend the admin UI, and the docs just point you toward building a plugin. But what happens next? How do you manage this new plugin alongside your main Strapi application without a clunky, manual build process?
After scouring forums and finding more questions than answers, I pieced together a solution that’s surprisingly simple and elegant. Here’s how to tame your local plugins using a clean monorepo structure.
Part 1: Creating Your Local Plugin
First, let’s get the basic plugin running. Strapi’s documentation provides a solid starting point.
-
Scaffold a new Strapi app if you haven’t already.
Terminal window npx create-strapi-app@latest my-project -
Generate your plugin using the official SDK. This command creates a dedicated
packages/my-local-pluginfolder, which is a complete Node.js project in itself.Terminal window npx @strapi/sdk-plugin init packages/my-local-plugin -
Register the plugin with your Strapi application. Create or edit
config/plugins.tsto tell Strapi where to find it.config/plugins.ts export default {'my-local-plugin': {enabled: true,resolve: './packages/my-local-plugin',},};
At this point, you can cd into packages/my-local-plugin and run npm run watch to auto-rebuild the plugin as you make changes. This is great for development, but it’s not a sustainable solution for a team or for deployment.
Part 2: The Monorepo Magic
Having to manually build sub-folders is cumbersome. The answer is to treat your entire Strapi project as a monorepo. We can do this easily with npm’s built-in workspaces feature.
-
Define your workspaces. In your main project’s
package.json, tell npm where to find your sub-packages.package.json // ...existing code..."workspaces": ["packages/*"],// ...existing code...Now, when you run
npm installfrom the root, npm will also install dependencies for your local plugin. You can test this by deleting allnode_modulesfolders and runningnpm installfrom your project’s root directory. -
Isolate your TypeScript configs. You don’t want the main Strapi TypeScript compiler to process your plugin’s source code; the plugin has its own build process. Add an
excluderule to your roottsconfig.json.tsconfig.json // ...existing code..."exclude": [// ...existing code..."packages/**",] -
Unify the build process. This is the key step. Instead of building each piece separately, you can run a single command from the root folder to build all workspaces. The
--workspacesflag tells npm to run thebuildscript in every package defined in yourworkspacesarray.Terminal window npm run build --workspacesThis command will call the
buildscript inpackages/my-local-plugin, creating itsdistfolder. -
Streamline with npm scripts. To make the process even smoother, let’s add some helper scripts to the root
package.json. This combines building the plugins and the main Strapi app into a single command.package.json // ...existing code..."scripts": {// ...existing code..."build:plugins": "npm run build --workspaces","build:strapi": "strapi build","build": "npm run build:plugins && npm run build:strapi","dev": "strapi develop","start": "strapi start",// ...existing code...},// ...existing code... -
Build and run. Now, you have a simple, unified workflow.
For development, run Strapi as usual, but remember to build the plugin first:
Terminal window npm run build:pluginsnpm run devFor production, the process is just as clean:
Terminal window npm run buildnpm run start
That’s it! With this setup, anyone can clone your project and get it running with a familiar npm install and npm run build. No more cd-ing into sub-folders or manual build steps. Just a simple, clean, and efficient workflow.