惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
T
Tenable Blog
Webroot Blog
Webroot Blog
L
Lohrmann on Cybersecurity
S
Securelist
S
Schneier on Security
NISL@THU
NISL@THU
Know Your Adversary
Know Your Adversary
C
Cybersecurity and Infrastructure Security Agency CISA
T
The Exploit Database - CXSecurity.com
L
LINUX DO - 热门话题
C
CXSECURITY Database RSS Feed - CXSecurity.com
O
OpenAI News
I
Intezer
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
TaoSecurity Blog
TaoSecurity Blog
S
Secure Thoughts
Application and Cybersecurity Blog
Application and Cybersecurity Blog
P
Privacy International News Feed
H
Hacker News: Front Page
N
Netflix TechBlog - Medium
M
MIT News - Artificial intelligence
博客园 - Franky
PCI Perspectives
PCI Perspectives
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Microsoft Azure Blog
Microsoft Azure Blog
MongoDB | Blog
MongoDB | Blog
L
LangChain Blog
P
Proofpoint News Feed
S
Security Affairs
WordPress大学
WordPress大学
The Last Watchdog
The Last Watchdog
S
SegmentFault 最新的问题
小众软件
小众软件
F
Full Disclosure
博客园 - 叶小钗
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
The Blog of Author Tim Ferriss
Simon Willison's Weblog
Simon Willison's Weblog
P
Palo Alto Networks Blog
Security Latest
Security Latest
P
Proofpoint News Feed
月光博客
月光博客
T
Tailwind CSS Blog
Scott Helme
Scott Helme
Hacker News - Newest:
Hacker News - Newest: "LLM"
Google Online Security Blog
Google Online Security Blog
T
Threat Research - Cisco Blogs
Help Net Security
Help Net Security
Project Zero
Project Zero

Nx Blog

Sharing Tailwind CSS Styles Across Apps in a Monorepo | Nx Blog How SiriusXM Stays Competitive by Iterating and Getting to Market Fast | Nx Blog Agentic Experience Is the New Developer Experience | Nx Blog Nx Joins the Linux Foundation and the Agentic AI Foundation | Nx Blog A Monorepo Is NOT a Monolith | Nx Blog Why we deleted (most of) our MCP tools | Nx Blog Teach Your AI Agent How to Work in a Monorepo | Nx Blog How Broadcom stays efficient and nimble with monorepos | Nx Blog Why Monorepos are King in the Age of AI | Nx Blog Nx 2026 Roadmap: Expanding Agent Autonomy, Improving Performance, Better Polyglot and More | Nx Blog End to End Autonomous AI Agent Workflows with Nx | Nx Blog Autonomous Agents at Scale | Nx Blog Scaling 700+ Projects: How Nx Became a 'No-Brainer' for Caseware | Nx Blog Configure Tailwind v4 with Angular in an Nx Monorepo | Nx Blog The Missing Multiplier for AI Agent Productivity | Nx Blog A Year of Nx Webinars | Nx Blog Wrapping Up 2025 | Nx Blog Nx 22.3 Release: Angular 21 Support, tsgo Compiler, and Prettier v3 | Nx Blog Nx Cloud Release: Agent Resource Usage | Nx Blog Nx Platform Outperforms DIY Cache by 5x | Nx Blog An Nx Carol: Past, Present, and Future of Your Monorepo | Nx Blog Nx 22.1 Release: Terminal UI on Windows, Storybook 10, Vitest 4, and more! | Nx Blog The Compounding Effect: How Nx Features Multiply Performance Gains | Nx Blog 10 Monorepo Myths Debunked: Separating Fact from Fiction | Nx Blog Nx Cloud Release: Enterprise Task Analytics | Nx Blog Watch and Rebuild Storybook Dependencies with Nx | Nx Blog Book - React for Enterprise: Timeless Architecture for Enterprise Apps | Nx Blog Beyond Remote Cache: Unlock 70% More CI Performance | Nx Blog Nx 22 Release: Expanding the build platform | Nx Blog What's the Point of Generating All This Code If You Can't Merge It? | Nx Blog What's New in Nx Self-Healing CI | Nx Blog Nx Highlights: Smarter AI integration, all-new graph UI, and big new versions of your favorite tools | Nx Blog Making the Case for Smarter Monorepos, and How to Not Get Fooled by Myths | Nx Blog S1ngularity - What Happened, How We Responded, What We Learned | Nx Blog Stop Babysitting Your PRs: Self-Healing CI Cuts Time to Green by 50% | Nx Blog UKG Unifies Their Codebase and Eliminates CI Overhead to Focus on Customer Value | Nx Blog How Git Worktrees Changed My AI Agent Workflow | Nx Blog Nx Cloud Workspace Graph: See Your Organization's Code Structure Like Never Before | Nx Blog Seamless Java Deployment in Nx Using Docker | Nx Blog Getting Mobile Into Your Monorepo: Android + Nx | Nx Blog Polyglot Projects Made Easy: Integrating Spring Boot into an Nx Workspace | Nx Blog The Journey of the Nx Plugin for Gradle: From Prototype to Production | Nx Blog Combining Predictability and Intelligence With Nx Generators and AI | Nx Blog A New UI For The Humble Terminal | Nx Blog Continuous tasks are a huge DX improvement | Nx Blog New and Improved Module Federation Experience with Nx | Nx Blog A New UI for Nx Migration | Nx Blog Custom Task Runners and Self-Hosted Caching Changes | Nx Blog Enterprise Angular Monorepo Patterns | Nx Blog Using Rspack with Angular | Nx Blog Angular Architecture Guide To Building Maintainable Applications at Scale | Nx Blog Modern Angular Testing with Nx | Nx Blog Nx Update: 20.5 | Nx Blog Are Monorepos the Answer to Better AI-Assisted Development? | Nx Blog Making Cursor Smarter with an MCP Server For Nx Monorepos | Nx Blog React Development for 2025 | Nx Blog Using Apollo GraphQL in an Nx Workspace | Nx Blog Angular State Management for 2025 | Nx Blog Tailoring Nx for Your Organization | Nx Blog Nx Cloud Pipelines Come To Nx Console | Nx Blog Define the relationship with monorepos | Nx Blog See your affected project graph in Nx Cloud | Nx Blog Handling CORS In Your Workspace | Nx Blog Improve your architecture and CI pipeline times with Nx projects | Nx Blog Announcing Nx 20 | Nx Blog Introducing Nx Powerpack | Nx Blog Nx 19.5 is here! Stackblitz, Bun, Incremental Builds for Vite, Gradle Test Atomizer | Nx Blog Introducing Explain with AI | Nx Blog Nx Enterprise Podcast Episode 2: Tine Kondo | Nx Blog Monorepos and CI can be a Mess - Here's How Nx and Nx Cloud Fixed It | Nx Blog Nx Enterprise Podcast Episode 1: Hicham El Hammouchi | Nx Blog Nx 19.0 Release!! | Nx Blog Manage Your Gradle Project using Nx | Nx Blog Making the Argument for Monorepos | Nx Blog Reliable CI. A new execution model fixing both flakiness and slowness | Nx Blog Monorepos - Why Speed Matters | Nx Blog Nx Agents Walkthrough: Effortlessly Fast CI Built for Monorepos | Nx Blog Launch Nx Week Recap | Nx Blog Versioning and Releasing Packages in a Monorepo | Nx Blog Fast, Effortless CI | Nx Blog Introducing @nx/nuxt Enhanced Nuxt.js Support in Nx | Nx Blog What if Nx Plugins Were More Like VSCode Extensions | Nx Blog Monorepos: the Benefits, Challenges, and Importance of Tooling Support | Nx Blog Nx — Highlights of 2023 | Nx Blog Nx 17.2 Update | Nx Blog Unit Testing Expo Apps With Jest | Nx Blog Nx Docs AI Assistant | Nx Blog State Management Nx React Native/Expo Apps with TanStack Query and Redux | Nx Blog Nx 17 has Landed | Nx Blog Nx Conf 2023 — Recap | Nx Blog Nx Raises $16M Series A | Nx Blog Introducing Playwright Support for Nx | Nx Blog Nx 16.8 Release!!! | Nx Blog Step-by-Step Guide to Creating an Expo Monorepo with Nx | Nx Blog Qwikify your Development with Nx | Nx Blog Create Your Own create-react-app CLI | Nx Blog Storybook Interaction Tests in Nx | Nx Blog Evergreen Tooling — More than Just CodeMods | Nx Blog Nx 16.5 Release!!! | Nx Blog A Practical Guide on Effective AI Use - AI as Your Peer Programmer | Nx Blog
Integrating Biome in 20 Minutes | Nx Blog
Philip Fulcher · 2025-09-17 · via Nx Blog

One question we get all the time is: "When are you going to support my tool of choice?" Whether it's a new linting tool, build tool, or framework, the truth is we just can't support everything with a team our size. But we do provide multiple tools to make that integration possible for you.

Today, we're going to walk through integrating Biome, a fast linter and formatter written in Rust, into an Nx workspace. We'll show you how to get started with Biome, apply what you already know about the Node ecosystem, and finally write a plugin that scales for large workspaces with Nx.

Why Biome?

Biome promises to be faster than ESLint and Prettier because it's written in Rust. And hey, who doesn't love using Rust to make things fast?

We're working with a workspace that looks like this: a React application that depends on a few libraries. This workspace is small, but the process we're walking through will scale to any size workspace.

A workspace graph showing an application depending on three libraries.

Start with What You Know: Using Biome Without Nx

Before thinking about Nx at all, let's see how far we can get knowing a little bit about Biome and a lot about the Node ecosystem.

Looking at the Biome getting started docs, we need to:

  1. Install the package:
npm install --save-dev @biomejs/biome
  1. Create the root configuration using their init command:

This creates a biome.json file at the root of our repo.

  1. Run the linter:

Without thinking about Nx at all, we've successfully installed Biome and run a lint across our entire workspace. That's a good starting point, but in a monorepo, we don't want to lint all files all the time. We want to lint individual projects.

Using npm Scripts

Normally, to avoid writing long commands every time, we'd reach for npm scripts. Let's add this to our root package.json:

{
  "scripts": {
    "biome-lint": "biome lint"
  }
}

Now we can run npm run biome-lint instead. But we're still linting the entire workspace.

Targeting Individual Projects

Since our workspace uses npm workspaces, every project has its own package.json. We can add the same script to individual project package files:

apps/biome-example/package.json

{
  "scripts": {
    "biome-lint": "biome lint"
  }
}

Now if we move into that directory and run npm run biome-lint, we're only linting that specific directory. When you run biome lint with no other options, it lints that directory and directories below it.

The Nx Integration You Didn't Realize Was There

Here's something you might not realize: we've already started an Nx integration. If you open Nx Console, you'll see that biome-lint is available as a target under the "npm scripts" area. Nx automatically incorporates npm scripts into available tasks.

Screenshot of Nx Console showing npm scripts available as targets

So while npm run biome-lint works, we can also run this through Nx:

npx nx biome-lint biome-example

This gives us the exact same result. We can copy this script to other packages, like our navigation library, and suddenly we can run biome-lint on multiple projects individually.

npx nx run-many -t biome-lint

Hold On, I'm Not Using NPM Workspaces!

While npm workspaces are the default for new Nx workspaces, there's a long history of workspaces created before that. Those workspaces use project.json files for project configuration. While you won't be able to use npm scripts like in npm workspaces, you can still create a target in your project.json files:

apps/biome-example/project.json

{
  "targets": {
    "biome-lint": {
      "command": "npx @biomejs/biome lint {projectRoot}"
    }
  }
}

You'll notice one small difference: {projectRoot} appended to the end of the command. We'll explain this part when we're creating our inferred task plugin later, because this configuration is exactly what you'll be creating as part of the plugin.

Adding Caching Support

One thing missing with this simple setup is caching. Nx doesn't know the biome-lint command is cacheable, and it doesn't know what to cache.

Right now, if we rerun nx biome-lint biome-example twice, it actually runs the lint every time. Ideally, we shouldn't rerun this lint if we haven't changed anything between successful runs.

To configure caching, let's open our nx.json and add target defaults for biome-lint:

{
  "targetDefaults": {
    "biome-lint": {
      "cache": true,
      "inputs": [
        "default",
        "^default",
        "{workspaceRoot}/biome.json",
        {
          "externalDependencies": ["@biomejs/biome"]
        }
      ]
    }
  }
}

How do we figure out these inputs? Steal them from another linting configuration that we know works. We can copy the inputs from ESLint's configuration using Nx Console. Find a project using eslint and scroll down to the "Inputs" area.

Screenshot of Nx Console showing the ability to copy inputs from a target

Click the copy button and modify the output for Biome:

  • Change eslint.config.mjs to biome.json
  • Remove ESLint-specific files we don't need
  • Update the external dependency to @biomejs/biome

Now when we run the task once, it gets cached. Running it a second time will read from cache instead of running again.

Creating an Nx Plugin for Scale

Copying targets to every package would be easy in a small workspace, but this process needs to scale to workspaces with hundreds of packages. Instead, let's create an Nx plugin that uses inferred tasks to create these targets automatically.

An inferred task is created by an Nx plugin by scanning your workspace for particular configuration files and adding targets to projects where it finds those files.

Setting Up the Plugin

First, install the Nx plugin tooling:

Then generate a new plugin:

npx nx g @nx/plugin:plugin --name=plugin --directory=plugin

This creates a plugin project at the root of our workspace. The important file is plugin/src/index.ts, which we'll fill with our Biome integration logic.

Understanding Plugin Structure

Looking at the Nx docs on extending the project graph, we can find a code example to paste into our index.ts:

import {
  CreateNodesContextV2,
  createNodesFromFiles,
  CreateNodesV2,
  readJsonFile,
} from '@nx/devkit';
import { dirname } from 'path';

export interface MyPluginOptions {}

export const createNodesV2: CreateNodesV2<MyPluginOptions> = [
  '**/project.json',
  async (configFiles, options, context) => {
    return await createNodesFromFiles(
      (configFile, options, context) =>
        createNodesInternal(configFile, options, context),
      configFiles,
      options,
      context
    );
  },
];

async function createNodesInternal(
  configFilePath: string,
  options: MyPluginOptions | undefined,
  context: CreateNodesContextV2
) {
  const projectConfiguration = readJsonFile(configFilePath);
  const root = dirname(configFilePath);

  // Project configuration to be merged into the rest of the Nx configuration
  return {
    projects: {
      [root]: projectConfiguration,
    },
  };
}

Let's take this example and make some changes:

import {
  CreateNodesContextV2,
  createNodesFromFiles,
  CreateNodesV2,
} from '@nx/devkit';
import { dirname } from 'path';

export interface MyPluginOptions {}

export const createNodesV2: CreateNodesV2<MyPluginOptions> = [
  // look for all package.json files in the workspace
  // (keep this as project.json if you're not using npm workspaces)
  '**/package.json',
  async (configFiles, options, context) => {
    return await createNodesFromFiles(
      (configFile, options, context) =>
        createNodesInternal(configFile, options, context),
      configFiles,
      options,
      context
    );
  },
];

async function createNodesInternal(
  configFilePath: string,
  options: MyPluginOptions | undefined,
  context: CreateNodesContextV2
) {
  const root = dirname(configFilePath);

  // Project configuration to be merged into the rest of the Nx configuration
  return {
    projects: {
      [root]: {
        targets: {
          'biome-lint': {
            // Nx target syntax to execute a command. More on {projectRoot} below
            command: 'npx @biomejs/biome lint {projectRoot}',
            cache: true,
            inputs: [
              'default',
              '^default',
              '{workspaceRoot}/biome.json',
              {
                externalDependencies: ['@biomejs/biome'],
              },
            ],
          },
        },
      },
    },
  };
}

You can now delete all of the configuration you added to your package.json and nx.json files. Instead, this plugin will search for all of your projects and add the biome-lint target to each project.

What about that weird `{projectRoot}` in the command?

Nx executes tasks in the context of the root of the workspace. If you were to just have a command of npx nx @biomejs/biome, it would execute that command in the root of the workspace and lint the entire workspace. {projectRoot} is a special token that will be replaced with the directory of the project you're running against. So now the command will lint the project directory, not the entire workspace.

Key Plugin Concepts

  • File pattern: We're looking for package.json files (since we're using npm workspaces)
  • Project root: The directory containing the configuration file
  • Command interpolation: {projectRoot} gets replaced with the actual project path
  • Caching configuration: Same inputs we defined earlier, now embedded in the plugin

Activating the Plugin

To activate our plugin, add it to the plugins array in nx.json:

{
  "plugins": ["@biome-example/plugin"]
}

If you open Nx Console, you'll see the biome-lint target is still available, but now it shows "created by @biome-example/plugin."

Screenshot of Nx Console showing biome-lint proviced by the plugin

Selective Application with Configuration Files

Adding Biome to every project might be too aggressive. Most teams want to do a gradual transition where some packages use Biome and others stick with ESLint (especially since Biome doesn't yet support the enforce module boundaries rule).

Using Biome's Nested Config Support

According to Biome's documentation on big projects, we can have multiple biome.json files: one at the root and nested ones in individual packages.

Let's add a biome.json to our biome-example app:

apps/biome-example/biome.json

{
  "root": false,
  "extends": "//"
}

This configuration:

  • Points to the root configuration with extends
  • Allows package-specific rule overrides
  • Gives us fine-grained control like we have with ESLint

Updating the Plugin

Now we can modify our plugin to look for biome.json files instead of package.json:

import {
  CreateNodesContextV2,
  createNodesFromFiles,
  CreateNodesV2,
} from '@nx/devkit';
import { dirname } from 'path';

export interface MyPluginOptions {}

export const createNodesV2: CreateNodesV2<MyPluginOptions> = [
  '**/biome.json',
  async (configFiles, options, context) => {
    return await createNodesFromFiles(
      (configFile, options, context) =>
        createNodesInternal(configFile, options, context),
      configFiles,
      options,
      context
    );
  },
];

async function createNodesInternal(
  configFilePath: string,
  options: MyPluginOptions | undefined,
  context: CreateNodesContextV2
) {
  const root = dirname(configFilePath);

  // because there is also a biome.json at the root of the workspace, we want to ignore that one
  // return an empty object if we're at the root so that we don't create a root project

  if (root === '.') {
    return {};
  }

  // Project configuration to be merged into the rest of the Nx configuration
  return {
    projects: {
      [root]: {
        targets: {
          'biome-lint': {
            command: 'npx @biomejs/biome lint {projectRoot}',
            cache: true,
            inputs: [
              'default',
              '^default',
              '{workspaceRoot}/biome.json',
              {
                externalDependencies: ['@biomejs/biome'],
              },
            ],
          },
        },
      },
    },
  };
}

Now the plugin only adds biome-lint targets to projects that have a biome.json file. This enables progressive adoption: teams can opt into Biome by adding configuration files where they want them.

PROTIP

During plugin development, Nx caches plugin compilation. Use NX_DAEMON=false to bypass this cache:

NX_DAEMON=false nx run-many --target=biome-lint

When you're ready for production, run nx reset to clear the cache and the plugin will work normally.

The Power of Custom Plugins

This demonstrates why writing your own plugin is often better than waiting for official support. When the Nx team writes a plugin, we have to account for many different use cases and allow extensive configuration. But you don't have to worry about any of that: you're one team that can run things one way.

Your plugin can be much less complex than anything the Nx team would create because you only need to solve your specific use case.

Expanding Your Plugin

From here, you could add more functionality:

  • Generators to create biome.json files in new projects
  • Format commands in addition to linting
  • Custom configuration options specific to your organization's needs

Since you already have your own plugin, you can easily extend it without waiting for external support.

Key Takeaways

  1. Start with what you know - Use your existing ecosystem knowledge before diving into Nx specifics
  2. Leverage npm scripts integration - Nx automatically picks up package.json scripts as targets
  3. Add caching incrementally - Start simple, then optimize with proper inputs and outputs
  4. Build plugins for scale - Manual configuration works for small teams, but plugins scale to hundreds of projects
  5. Your plugin can be simpler - You don't need to handle every use case like official plugins do

Next Steps

Ready to build your own plugin? Here are some tools to support any tool in your workspace that the Nx team doesn't officially support:

Writing Nx plugins isn't as intimidating as it seems. With the same tools we use internally, you can integrate any tool into your Nx workspace. Stop waiting for official support and start building the developer experience your team needs.

Learn more: