nx-import
Quick Start
nx importbrings code from a source repository or folder into the current workspace, preserving commit history.- After nx
22.6.0,nx importresponds with .ndjson outputs and follow-up questions. For earlier versions, always run with--no-interactiveand specify all flags directly. - Run
nx import --helpfor available options. - Make sure the destination directory is empty before importing.
EXAMPLE: target has
libs/utilsandlibs/models; source haslibs/uiandlibs/data-access— you cannot importlibs/intolibs/directly. Import each source library individually.
Primary docs:
- https://nx.dev/docs/guides/adopting-nx/import-project
- https://nx.dev/docs/guides/adopting-nx/preserving-git-histories
Read the nx docs if you have the tools for it.
Import Strategy
Subdirectory-at-a-time (nx import <source> apps --source=apps):
- Recommended for monorepo sources — files land at top level, no redundant config
- Caveats: multiple import commands (separate merge commits each); dest must not have conflicting directories; root configs (deps, plugins, targetDefaults) not imported
- Directory conflicts: Import into alternate-named dir (e.g.
imported-apps/), then rename
Whole repo (nx import <source> imported --source=.):
- Only for non-monorepo sources (single-project repos)
- For monorepos, creates messy nested config (
imported/nx.json,imported/tsconfig.base.json, etc.) - If you must: keep imported
tsconfig.base.json(projects extend it), prefix workspace globs and executor paths
Directory Conventions
- Always prefer the destination's existing conventions. Source uses
libs/but dest usespackages/? Import intopackages/(nx import <source> packages/foo --source=libs/foo). - If dest has no convention (empty workspace), ask the user.
Application vs Library Detection
Before importing, identify whether the source is an application or a library:
- Applications: Deployable end products. Common indicators:
- Frontend:
next.config.*,vite.config.*with a build entry point, framework-specific app scaffolding (CRA, Angular CLI app, etc.) - Backend (Node.js): Express/Fastify/NestJS server entrypoint, no
"exports"field inpackage.json - JVM: Maven
pom.xmlwith<packaging>jar</packaging>or<packaging>war</packaging>and amainclass; Gradleapplicationplugin ormainClasssetting - .NET:
.csproj/.fsprojwith<OutputType>Exe</OutputType>or<OutputType>WinExe</OutputType> - General: Dockerfile, a runnable entrypoint, no public API surface intended for import by other projects
- Frontend:
- Libraries: Reusable packages consumed by other projects. Common indicators:
"main"/"exports"inpackage.json, Maven/Gradle packaging as a library jar, .NET<OutputType>Library</OutputType>, named exports intended for import by other packages.
Destination directory rules:
- Applications →
apps/<name>. Check workspace globs (e.g.pnpm-workspace.yaml,workspacesin rootpackage.json) for an existingapps/*entry.- If
apps/*is not present, add it before importing: update the workspace glob config and commit (or stage) the change. - Example:
nx import <source> apps/my-app --source=packages/my-app
- If
- Libraries → follow the dest's existing convention (
packages/,libs/, etc.).
Common Issues
pnpm Workspace Globs (Critical)
nx import adds the imported directory itself (e.g. apps) to pnpm-workspace.yaml, NOT glob patterns for packages within it. Cross-package imports will fail with Cannot find module.
Fix: Replace with proper globs from the source config (e.g. apps/*, libs/shared/*), then pnpm install.
Root Dependencies and Config Not Imported (Critical)
nx import does NOT merge from the source's root:
dependencies/devDependenciesfrompackage.jsontargetDefaultsfromnx.json(e.g."@nx/esbuild:esbuild": { "dependsOn": ["^build"] }— critical for build ordering)namedInputsfromnx.json(e.g.productionexclusion patterns for test files)- Plugin configurations from
nx.json
Fix: Diff source and dest package.json + nx.json. Add missing deps, merge relevant targetDefaults and namedInputs.
TypeScript Project References
After import, run nx sync --yes. If it reports nothing but typecheck still fails, nx reset first, then nx sync --yes again.
Explicit Executor Path Fixups
Inferred targets (via Nx plugins) resolve config relative to project root — no changes needed. Explicit executor targets (e.g. @nx/esbuild:esbuild) have workspace-root-relative paths (main, outputPath, tsConfig, assets, sourceRoot) that must be prefixed with the import destination directory.
Plugin Detection
- Whole-repo import:
nx importdetects and offers to install plugins. Accept them. - Subdirectory import: Plugins NOT auto-detected. Manually add with
npx nx add @nx/PLUGIN. Checkinclude/excludepatterns — defaults won't match alternate directories (e.g.apps-beta/). - Run
npx nx resetafter any plugin config changes.
Redundant Root Files (Whole-Repo Only)
Whole-repo import brings ALL source root files into the dest subdirectory. Clean up:
pnpm-lock.yaml— stale; dest has its own lockfilepnpm-workspace.yaml— source workspace config; conflicts with destnode_modules/— stale symlinks pointing to source filesystem.gitignore— redundant with dest root.gitignorenx.json— source Nx config; dest has its ownREADME.md— optional; keep or remove
Don't blindly delete tsconfig.base.json — imported projects may extend it via relative paths.
Root ESLint Config Missing (Subdirectory Import)
Subdirectory import doesn't bring the source's root eslint.config.mjs, but project configs reference ../../eslint.config.mjs.
Fix order:
- Install ESLint deps first:
pnpm add -wD eslint@^9 @nx/eslint-plugin typescript-eslint(plus framework-specific plugins) - Create root
eslint.config.mjs(copy from source or create with@nx/eslint-pluginbase rules) - Then
npx nx add @nx/eslintto register the plugin innx.json
Install typescript-eslint explicitly — pnpm's strict hoisting won't auto-resolve this transitive dep of @nx/eslint-plugin.
ESLint Version Pinning (Critical)
Pin ESLint to v9 (eslint@^9.0.0). ESLint 10 breaks @nx/eslint and many plugins with cryptic errors like Cannot read properties of undefined (reading 'version').
@nx/eslint may peer-depend on ESLint 8, causing the wrong version to resolve. If lint fails with Cannot read properties of undefined (reading 'allow'), add pnpm.overrides:
{ "pnpm": { "overrides": { "eslint": "^9.0.0" } } }
Dependency Version Conflicts
After import, compare key deps (typescript, eslint, framework-specific). If dest uses newer versions, upgrade imported packages to match (usually safe). If source is newer, may need to upgrade dest first. Use pnpm.overrides to enforce single-version policy if desired.
Module Boundaries
Imported projects may lack tags. Add tags or update @nx/enforce-module-boundaries rules.
Project Name Collisions (Multi-Import)
Same name in package.json across source and dest causes MultipleProjectsWithSameNameError. Fix: Rename conflicting names (e.g. @org/api → @org/teama-api), update all dep references and import statements, pnpm install. The root package.json of each imported repo also becomes a project — rename those too.
Workspace Dep Import Ordering
pnpm install fails during nx import if a "workspace:*" dependency hasn't been imported yet. File operations still succeed. Fix: Import all projects first, then pnpm install --no-frozen-lockfile.
.gitkeep Blocking Subdirectory Import
The TS preset creates packages/.gitkeep. Remove it and commit before importing.
Frontend tsconfig Base Settings (Critical)
The TS preset defaults (module: "nodenext", moduleResolution: "nodenext", lib: ["es2022"]) are incompatible with frontend frameworks (React, Next.js, Vue, Vite). After importing frontend projects, verify the dest root tsconfig.base.json:
moduleResolution: Must be"bundler"(not"nodenext")module: Must be"esnext"(not"nodenext")lib: Must include"dom"and"dom.iterable"(frontend projects need these)jsx:"react-jsx"for React-only workspaces, per-project for mixed frameworks
For subdirectory imports, the dest root tsconfig is authoritative — update it. For whole-repo imports, imported projects may extend their own nested tsconfig.base.json, making this less critical.
If the dest also has backend projects needing nodenext, use per-project overrides instead of changing the root.
Gotcha: TypeScript does NOT merge lib arrays — a project-level override replaces the base array entirely. Always include all needed entries (e.g. es2022, dom, dom.iterable) in any project-level lib.
@nx/react Typings for Libraries
React libraries generated with @nx/react:library reference @nx/react/typings/cssmodule.d.ts and @nx/react/typings/image.d.ts in their tsconfig types. These fail with Cannot find type definition file unless @nx/react is installed in the dest workspace.
Fix: pnpm add -wD @nx/react
Jest Preset Missing (Subdirectory Import)
Nx presets create jest.preset.js at the workspace root, and project jest configs reference it (e.g. ../../jest.preset.js). Subdirectory import does NOT bring this file.
Fix:
- Run
npx nx add @nx/jest— registers@nx/jest/plugininnx.jsonand updatesnamedInputs - Create
jest.preset.jsat workspace root (seereferences/JEST.mdfor content) —nx addonly creates this when a generator runs, not on barenx add - Install test runner deps:
pnpm add -wD jest jest-environment-jsdom ts-jest @types/jest - Install framework-specific test deps as needed (see
references/JEST.md)
For deeper Jest issues (tsconfig.spec.json, Babel transforms, CI atomization, Jest vs Vitest coexistence), see references/JEST.md.
Target Name Prefixing (Whole-Repo Import)
When importing a project with existing npm scripts (build, dev, start, lint), Nx plugins auto-prefix inferred target names to avoid conflicts: e.g. next:build, vite:build, eslint:lint.
Fix: Remove the Nx-rewritten npm scripts from the imported package.json, then either:
- Accept the prefixed names (e.g.
nx run app:next:build) - Rename plugin target names in
nx.jsonto use unprefixed names
Non-Nx Source Issues
When the source is a plain pnpm/npm workspace without nx.json.
npm Script Rewriting (Critical)
Nx rewrites package.json scripts during init, creating broken commands (e.g. vitest run → nx test run). Fix: Remove all rewritten scripts — Nx plugins infer targets from config files.
noEmit → composite + emitDeclarationOnly (Critical)
Plain TS projects use "noEmit": true, incompatible with Nx project references.
Symptoms: "typecheck target is disabled because one or more project references set 'noEmit: true'" or TS6310.
Fix in all imported tsconfigs:
- Remove
"noEmit": true. If inherited via extends chain, set"noEmit": falseexplicitly. - Add
"composite": true,"emitDeclarationOnly": true,"declarationMap": true - Add
"outDir": "dist"and"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo" - Add
"extends": "../../tsconfig.base.json"if missing. Remove settings now inherited from base.
Stale node_modules and Lockfiles
nx import may bring node_modules/ (pnpm symlinks pointing to the source filesystem) and pnpm-lock.yaml from the source. Both are stale.
Fix: rm -rf imported/node_modules imported/pnpm-lock.yaml imported/pnpm-workspace.yaml imported/.gitignore, then pnpm install.
ESLint Config Handling
- Legacy
.eslintrc.json(ESLint 8): Delete all.eslintrc.*, remove v8 deps, create flateslint.config.mjs. - Flat config (
eslint.config.js): Self-contained configs can often be left as-is. - No ESLint: Create both root and project-level configs from scratch.
TypeScript paths Aliases
Nx uses package.json "exports" + pnpm workspace linking instead of tsconfig "paths". If packages have proper "exports", paths are redundant. Otherwise, update paths for the new directory structure.
Technology-specific Guidance
Identify technologies in the source repo, then read and apply the matching reference file(s).
Available references:
references/ESLINT.md— ESLint projects: duplicatelint/eslint:linttargets, legacy.eslintrc.*linting generated files, flat config.cjsself-linting,typescript-eslintv7/v9 peer dep conflict, mixed ESLint v8+v9 in one workspace.references/GRADLE.mdreferences/JEST.md— Jest testing:@nx/jest/pluginsetup, jest.preset.js, testing deps by framework, tsconfig.spec.json, Jest vs Vitest coexistence, Babel transforms, CI atomization.references/NEXT.md— Next.js projects:@nx/next/plugintargets,withNx, Next.js TS config (noEmit,jsx: "preserve"), auto-installing deps via wrong PM, non-Nxcreate-next-appimports, mixed Next.js+Vite coexistence.references/TURBOREPO.mdreferences/VITE.md— Vite projects (React, Vue, or both):@nx/vite/plugintypecheck target,resolve.alias/__dirnamefixes, framework deps, Vue-specific setup, mixed React+Vue coexistence.