Nx is a superset of Turborepo, so migrating requires minimal effort. The diff is tiny: an nx.json file (equivalent to turbo.json), the nx package added to package.json, and a .gitignore entry for the Nx cache. No changes to your existing projects or scripts are needed.
Easy automated migration example
Section titled “Easy automated migration example”- Let's create a new Turborepo workspace using the recommended
create-turbocommand:
npx create-turbo@latest- Once that is finished, literally all we need to do make it a valid Nx workspace is run
nx init:
npx nx@latest initThat's it! As you can see, the diff is tiny:
.gitignore | 3 +++ # Ignore the Nx cachepackage.json | 1 + # Add the "nx" packagepackage-lock.json |nx.json | # Equivalent to turbo.json- An
nx.jsonfile that is equivalent to theturbo.jsonfile was added - The
package.jsonfile was updated to add thenxdev dependency (and the package-lock.json was updated accordingly) - The .gitignore entry for the Nx cache was added automatically
It's important to remember that Nx is a superset of Turborepo, it can do everything Turborepo can do and much more, so there is absolutely no special configuration needed for Nx, it just works on the Turborepo workspace.
Example: Basic configuration comparison
Section titled “Example: Basic configuration comparison”To help with understanding the new nx.json file, let's compare it to the turbo.json file:
{ "$schema": "https://turbo.build/schema.json", // Nx will automatically use an appropriate terminal output style for the tasks you run "ui": "tui", "tasks": { "build": { // This syntax of build depending on the build of its dependencies using ^ is the same // in Nx "dependsOn": ["^build"], // Inputs and outputs are in Turborepo are relative to a particular package, whereas in Nx they are consistently from the workspace root and it therefore has {projectRoot} and {projectName} helpers "inputs": ["$TURBO_DEFAULT$", ".env*"], "outputs": [".next/**", "!.next/cache/**"] }, "lint": { // Turborepo tasks are assumed to be cacheable by default, so there is no recognizable configuration here. In Nx, the "cache" value is clearly set to true. "dependsOn": ["^lint"] }, "check-types": { "dependsOn": ["^check-types"] }, "dev": { // Nx uses "continuous" instead of "persistent" "persistent": true } }}After running nx init, you'll automatically have an equivalent nx.json:
{ "$schema": "./node_modules/nx/schemas/nx-schema.json", "targetDefaults": { "build": { "dependsOn": ["^build"], "inputs": ["{projectRoot}/**/*", "{projectRoot}/.env*"], "outputs": ["{projectRoot}/.next/**", "!{projectRoot}/.next/cache/**"], "cache": true }, "lint": { "dependsOn": ["^lint"], "cache": true }, "check-types": { "dependsOn": ["^check-types"], "cache": true }, "dev": { "continuous": true } }}Configuration migration guide
Section titled “Configuration migration guide”Most settings in the old turbo.json file can be converted directly into nx.json equivalents. Here's how to map each configuration property:
Global configuration
Section titled “Global configuration”| Turborepo Property | Nx Equivalent |
|---|---|
cacheDir | Set in cacheDirectory |
daemon | Use NX_DAEMON=false or set useDaemonProcess: false in nx.json |
envMode | Nx core does not block any environment variables. See React and Angular guides |
globalDependencies | Add to the sharedGlobals namedInput |
globalEnv | Add to the sharedGlobals namedInput as an env input |
globalPassThroughEnv | N/A. See Defining Environment Variables |
remoteCache | See Nx Replay |
ui | Nx will intelligently pick the most appropriate terminal output style, but it can be overridden with --output-style |
Task configuration
Section titled “Task configuration”| Turborepo Property | Nx Equivalent |
|---|---|
extends | N/A. Projects always extend targetDefaults from nx.json |
dependsOn | Same syntax |
env | Define env inputs |
passThroughEnv | N/A. See Defining Environment Variables |
outputs | Similar syntax |
cache | Similar syntax |
inputs | Similar syntax |
outputLogs | Use --output-style |
persistent | Use continuous |
interactive | N/A. Tasks marked continuous can accept stdin automatically. |
Command equivalents
Section titled “Command equivalents”Here's how Turborepo commands map to Nx:
| Turborepo Command | Nx Equivalent |
|---|---|
turbo run test lint build | nx run-many -t test lint build |
turbo run build --affected | nx affected -t build |
turbo devtools | nx graph for full interactive experience, also available in Nx Console |
--cache-dir | Set in nx.json under cacheDirectory |
--concurrency | --parallel |
--continue | Use --nx-bail with the inverse value |
--cpuprofile | Use NX_PROFILE=profile.json |
--cwd | Available in run-commands executor |
--daemon | Use NX_DAEMON=false or set useDaemonProcess: false |
--dry-run | Use nx show target <project>:<target> --inputs --outputs to preview inputs and outputs (available since 22.6.0+) |
--env-mode | See React and Angular guides |
--filter | Use lots of advanced project matcher syntax like -p admin-* or -p tag:api-* |
--force | nx reset and then run the command again |
--framework-inference | N/A. Nx plugins infer tasks automatically as a first class feature |
--global-deps | Use the sharedGlobals namedInput. Nx is far more flexible with composable namedInputs |
--graph | Similar syntax or nx graph for full interactive experience |
--heap | N/A. Use --verbose |
--ignore | Use .nxignore or .gitignore |
--log-order | Use --output-style |
--no-cache | Use --skip-nx-cache |
--output-logs | Use --output-style |
--only | N/A |
--parallel | N/A |
--preflight | N/A |
--summarize | N/A |
--token | Set Nx Cloud CI Access Token |
--team | See --token for Nx Cloud workspace selection |
--trace | N/A. Use --verbose |
--verbosity | Use --verbose |
turbo gen | Use nx generate |
turbo login | nx login - Create an Nx Cloud account |
turbo link | nx connect - Connect a workspace to an Nx Cloud account |
For a complete list of Nx commands and options, see the Nx CLI documentation.