What you'll learn
- Paths for greenfield monorepos, polyrepo migration, slow CI and boundary enforcement
- Match pains like everything-builds or tangled imports to a fix
- Thin apps, focused libs and dependency direction
- Affected builds and caching so CI stays fast as the graph grows
Nx turns code colocation into an operational monorepo platform: task caching, affected analysis, module boundaries and a project graph. A plain git monorepo without those pieces usually recreates polyrepo pain inside one repository.
Use the path picker when you know your situation. Use the pain matcher when CI or imports already hurt. For import-layer practice without leaving ReactField, use the ImportBoundaryChecker on Project Standards.

How to use this page
- Pick a workspace path if you are starting or migrating.
- Match a pain if CI or imports already hurt.
- Keep apps thin and libs focused.
- Enforce boundaries with tags, not hope.
Pick an Nx workspace path
Try it: Nx workspace path
Pick your situation. You get an ordered approach, why it fits, and a first-week plan.
Goal
Your path
Thin apps, focused libs, boundaries from day one
Start with one app and a few libs. Add caching and affected commands before the repo is large enough to hurt.
- Nx getting started
Official intro and workspace mental model.
- Applications and libraries
Apps as composition roots; libs hold product code.
- Module boundary rules
Tag and lint rules before imports tangle.
- Project graph
See the dependency picture early.
First week
Scaffold one web app and two libs (ui + feature). Run nx graph. Add tags for type and scope.
Tip: monorepo success is tooling plus boundaries, not just putting folders in one git repo.
Checkpoint
You are starting a greenfield Nx workspace. Where should most product code live?
Pick an answer, or press A, B, C on your keyboard.
Match an Nx pain

Try it: match an Nx pain
Choose the pain you feel. The matcher points to a primary fix and an optional alternate.
Pain
The pain
Every PR runs lint, test and build for the entire workspace.
Start here
Nx affectedAffected analysis runs only changed projects and their dependents. Pair it with caching.
Tip: practice forbidden imports on Project Standards with the ImportBoundaryChecker.
Key takeaway
Monorepo success depends on tooling and boundaries, not on repository shape alone.
Why monorepos and why Nx
When you manage multiple related projects, you usually choose between:
- Polyrepo: each project has its own repository and release cycle.
- Monorepo: all related projects live in one repository.
Neither is universally better. The right choice depends on team size, coupling, release model and tooling maturity.
| Aspect | Polyrepo | Monorepo |
|---|---|---|
| Code sharing | Publish and version packages | Direct imports, always in sync |
| Cross-project changes | Multiple PRs, coordination overhead | Single atomic commit |
| Dependency updates | Repeated across repos | One update path |
| Refactoring | Cross-repo complexity | Centralized, IDE-friendly |
Plain monorepos without platform tooling usually create slow CI, weak boundaries, inconsistent commands and hard onboarding.
Nx addresses that with:
- Task caching: reuses prior build, test and lint outputs
- Affected analysis: runs only what changed and downstream dependents
- Module boundaries: enforces architecture through lint rules
- Dependency graph: makes impact and coupling visible

# visualize project relationships
npx nx graph
# run tasks only for changed projects
npx nx affected --target=build
npx nx affected --target=test
# run one project's target
npx nx run web-app:serveWorkspace layout
Nx works best with a clear split:
apps/for thin deployable entry pointslibs/for most business logic, features, UI and shared utilities
Think of apps as composition roots. Libraries hold the actual product code.
// apps/web-app/src/app/page.tsx
import { HomePage } from '@org/web-modules/feature-home'
export default function Page() {
return <HomePage />
}
Suggested shape:
monorepo/
|-- apps/ # Thin, deployable entry points
| |-- web-app/
| |-- mobile-app/
| |-- api-server/
| `-- admin-dashboard/
|
|-- libs/ # Most code lives here
| |-- web-modules/
| |-- ui/
| |-- shared/
| `-- db/
|
|-- tools/
|-- nx.json
|-- tsconfig.base.json
`-- package.jsonYou can also scope libs/ by ownership: libs/client/*, libs/admin/*, libs/shared/*.
Library types
Libraries stay clearer when each one has a single concern and consistent naming.
- Feature libraries (
feature-*): route-level business logic and smart components - UI libraries (
ui-*): reusable presentational components - Data-access libraries (
data-access-*): API clients, query logic, state adapters - Utility libraries (
util-*): pure helpers, formatters, validators, constants
libs/
|-- web/
| |-- feature-auth/
| |-- feature-dashboard/
| `-- data-access-api/
|-- shared/
| |-- ui-kit/
| |-- util-format/
| `-- types/
`-- admin/
|-- feature-users/
`-- data-access-admin-api/Feature modules keep domain logic together:
libs/web/feature-auth/src/
|-- components/
|-- hooks/
|-- services/
|-- models/
|-- routes/
`-- index.tsUse barrel index.ts files as the public API and hide internals.
Module boundaries

Without boundaries, monorepos become tightly coupled. Safe default direction:
apps/* -> feature-* -> data-access-* -> util-* / typesAvoid reverse imports (for example util-* importing feature-*).
- Add tags per project (domain, type, scope)
- Enforce import rules in linting
- Review
nx graphfor accidental cycles before merge
Practice the same idea interactively on Project Standards. Do not treat folders alone as architecture.
Checkpoint
CI runs lint, test and build for every project on every PR. What should you wire first?
Pick an answer, or press A, B, C on your keyboard.
Affected builds and CI
A scalable Nx workflow focuses on fast local iteration and selective CI.
# local: one app or one lib
npx nx run web-app:serve
npx nx run web-feature-auth:test
npx nx affected --target=lint
# CI: changed projects and dependents only
npx nx affected --target=lint --base=origin/main --head=HEAD
npx nx affected --target=test --base=origin/main --head=HEAD
npx nx affected --target=build --base=origin/main --head=HEADTips:
- Keep targets standardized (
build,test,lint,typecheck) across projects - Prefer smaller libraries with clear APIs over giant shared buckets
- Use caching aggressively in CI and local dev
The key habit
Thin apps, focused libs, tags that lint can enforce, and affected CI before the graph is huge.
Nx checklist
- Apps are thin composition roots
- Libs are named by type (feature, ui, data-access, util)
- Dependency direction is documented and linted
-
nx graphis reviewed for cycles on large changes - CI uses
nx affectedagainst the default branch - Task caching is enabled where it helps
Further reading
- Project Standards
- Frameworks & Build Tools
- Bundle Size Optimization
- Ecosystem
- Automated Testing
- Nx getting started (opens in a new tab)
Conclusion
Nx works when architecture and tooling are one system. Monorepo shape defines collaboration. Library boundaries define health over time. Caching and affected commands define delivery speed.
Start simple. Enforce boundaries early. Scale library granularity with the team, not ahead of it.
The key takeaway
Treat Nx as a platform for boundaries and selective work, not as a folder convention alone.
Check your understanding
Where should most product code live in a greenfield Nx workspace?
Pick an answer, or press A, B, C on your keyboard.
Recap
What to remember from this article
Sections read
0
Questions
0
none answered yet
Quiz
0
not taken yet
Up next: Automated Testing · 7 min
Continue