Blog
Philip Fulcher
December 10, 2024

Tailoring Nx for Your Organization

Tailoring Nx for Your Organization

Maintaining a scalable and maintainable monorepo is one of the biggest challenges for growing teams. Standards are essential for keeping code consistent, but relying on documentation and human diligence often falls short. The real challenge is how to enforce those standards effectively and sustainably.

This is where Nx comes in. Nx provides "drop-in" plugins that enhance the developer experience in monorepos, offering solutions for everything from code generation to automating caching and task execution. One of the biggest benefits is how easily you can create custom plugins which allow you to automate your standards based on your organization's needs.

In this article, we’ll explore how Nx plugins, including custom ones, can help you maintain scalable monorepos while simplifying workflows for your team.

What are Nx Plugins and why do you need them?

Nx plugins reduce the overhead of using specific tools or frameworks in a monorepo by automating common workflows. They are essentially NPM packages with a structure and metadata that Nx can interpret. A plugin may include:

  • Generators: For scaffolding code.
  • Executors: For automating tasks like building, testing, or deploying.
  • Migrations: For updating codebases, similar to codemods.

You’re not required to use Nx plugins, but they can significantly simplify your workflows. For example, they automate repetitive tasks and enforce consistency across your projects. You can explore the Nx Plugin Registry to see the available plugins, including those built by the Nx core team and contributions from the community.

Installing a plugin is straightforward. Use the nx add command to integrate a plugin into your workspace. For example:

nx add @nx/playwright

The plugins provided by the Nx team are usually designed to cover general use cases, like setting up popular frameworks or tools. However, the real power of lies in creating your own plugins—or extending existing ones—to automate your organization’s specific workflows.

Creating a custom plugin might sound intimidating, but it’s simpler than you think. The @nx/devkit package provides utilities to help you build functionality just like the Nx core team. You’re not starting from scratch; you’re leveraging an established API designed to make the process accessible and efficient.

Getting started with custom plugins

First, install the @nx/plugin package which provides useful code scaffolding features for creating a new custom plugin.

nx add @nx/plugin

Once installed, run the following generator that ships with the @nx/plugin package:

nx g plugin packages/nx-plugin --importPath=@org/nx-plugin

Also, make sure to check out our extensive instructions in our docs for building plugins, or you can jump right to the API for @nx/plugin.

Setting up custom generators

Creating custom generators is the biggest bang-for-your-buck change you can make using custom plugins. The generators distributed with Nx plugins have a wide set of options that allow you to fine-tune their output to meet your needs. But if you have decided on certain rules for your organization, such as a directory structure or project tag schema, it can be easy to forget some of your options when running a generator.

Custom generators are an easy way to enforce how generators from Nx plugins are used. For example:

nx generate @nx/react:library --name shared-util --directory libs/shared/util --importPath="@org/shared/util" --tags=type:util,scope:shared

To understand the right options for this generator, the engineer needs to remember:

  • the project naming convention based on the type of library and its scope
  • the directory structure based on nested folders for scope
  • the import naming convention
  • tag name convention based on type and scope

This is a lot to remember! Even the most diligent engineer will forget one of these or miss a typo. A custom generator can solve this problem. Consider a custom generator for your organization that accepts options like this:

nx generate @org/plugin:library --name util --scope shared --type util

This command runs our hypothetical custom generator and provides it with a scope and type for the library. This custom generator takes well-defined options for your org and translates them into the options needed for the underlying generator. A snippet of that would look something like this:

1import { Tree } from '@nx/devkit'; 2import { Linter } from '@nx/eslint'; 3import { libraryGenerator as reactLibraryGenerator } from '@nx/react'; 4import { LibraryGeneratorSchema } from './schema'; 5 6export async function libraryGenerator( 7 tree: Tree, 8 options: LibraryGeneratorSchema // schema with strong types for scope and type 9) { 10 // create project name based on submitted name and type 11 const projectName = `${options.type}-${options.name}`; 12 13 // determine directory based on scope and project name 14 const directory = joinPathFragments('libs', options.scope, projectName); 15 16 // determine import path based on scope and project name 17 const importPath = `@org/${scope}/${projectName}`; 18 19 // run the @nx/react library generator with options set 20 const callbackAfterFilesUpdated = await reactLibraryGenerator(tree, { 21 name: projectName, 22 directory, 23 importPath, 24 tags: [`type:${options.type}`, `scope:${shared}`], 25 linter: Linter.EsLint, 26 style: 'css', 27 unitTestRunner: 'vitest', 28 }); 29 30 return callbackAfterFilesUpdated; 31} 32 33export default libraryGenerator; 34

This example of a generator doesn’t do much on its own, but it pre-populates options for the underlying generator. This can already be a huge gain in terms of ensuring consistency as it:

  • Names the project appropriately
  • Creates the correct import path
  • Places new projects in the correct directory
  • Adds the proper tags to the project
  • Selects the correct set of tools

All of that without having to refer to your documentation on how to run the generator the right way. And most of the functionality is provided by Nx! You only need to provide some logic specific to your organization.

These generators will also show up in Nx Console alongside the other generators from Nx plugins, so engineers will have quick access.

Publish to your org with nx release

Nx plugins that are located within an existing Nx workspace can be run directly, without the need to build, bundle, or package. They just work and respond to changes just as quickly as any other project in your workspace.

Since we’re the monorepo people, it might seem wild to suggest that you would have multiple monorepos in your organization; but this is a common scenario, and we whole-heartedly support it.

To support consistency across your org, you can publish this plugin so that all of your Nx workspaces share the same generators and inferred tasks you just created. This makes onboarding any new Nx workspace easier and more consistent. The generator for your plugin will include configuration for nx release so that you’re ready to publish immediately:

1nx release --first-release 2

If you want to test your package by publishing locally, your project will also be set up with a Verdaccio configuration that allows you to run a local registry for testing your new plugin locally:

1nx local-registry 2

Presets to create your workspace the way you want it, every time

If you’re publishing a plugin for multiple workspaces in your organization, you’ll want those new workspaces to be created as consistently as your projects. When you create an Nx workspace, you might use a preset like this:

npx create-nx-workspace@latest react-monorepo --preset=react-monorepo

You can create this same type of preset for your org, so that new workspaces are set up the same way each time:

npx create-nx-workspace@latest react-monorepo --preset=org-react

Better yet, you can create your own install package, so calling create-org-workspace becomes your new standard instead of calling create-nx-workspace:

npx create-org-workspace@latest react-monorepo --framework=react

What else can a custom plugin provide?

Your plugin can provide anything an existing Nx plugin provides. This includes, but is not limited to:

Start consistent, stay consistent

Small changes to how Nx is used in your organization can make a big difference. All of these options to tailor Nx to your organization make the right thing to do, the easy thing to do. When your tooling is easier to use than doing it wrong, your engineers are more likely to adopt conventions and maintain them.

Learn More

Also, make sure to check out: