This tutorial walks you through creating a React monorepo with Nx. You'll build a small example application to understand the core concepts and workflows.
What you'll learn:
- How to structure multiple React apps and libraries in a single repository
- How Nx's caching speeds up your local development and CI pipelines
- How to run builds, tests, and serve commands efficiently across multiple projects
- How to share React components and hooks between applications
- How to fix CI failures directly from your editor with Nx Cloud
Prerequisite: Tutorial Setup
Section titled “Prerequisite: Tutorial Setup”Step 1: Creating a new Nx React workspace
Section titled “Step 1: Creating a new Nx React workspace”Let's create your workspace with our React preset to get started quickly.
Step 2: Verify Your Setup
Section titled “Step 2: Verify Your Setup”Please verify closely that you have the following setup:
- A new Nx workspace on your local machine
- A corresponding GitHub repository for the workspace with a
.github/workflows/ci.ymlpipeline preconfigured - You completed the full Nx Cloud onboarding and you now have a Nx Cloud dashboard that is connected to your example repository on GitHub.
You should see your workspace in your Nx Cloud organization.

If you do not see your workspace in Nx Cloud then please follow the steps outlined in the Nx Cloud setup.
This is important for using remote caching and self-healing in CI later in the tutorial.
Explore the Nx Workspace Setup
Section titled “Explore the Nx Workspace Setup”Let's take a look at the structure of our new Nx workspace:
Directoryacme/
Directory.github/
Directoryworkflows/
- ci.yml
Directoryapps/
Directorydemo/
- …
- README.md
- eslint.config.mjs
- nx.json
- package-lock.json
- package.json
- tsconfig.base.json
- tsconfig.json
- vitest.workspace.ts
Here are some files that might jump to your eyes:
- The
.nxfolder is where Nx stores local metadata about your workspaces using the Nx Daemon. - The
nx.jsonfile contains configuration settings for Nx itself and global default settings that individual projects inherit. - The
.github/workflows/ci.ymlfile preconfigures your CI in GitHub Actions to run build and test through Nx.
Now, let's build some features and see how Nx helps get us to production faster.
Serving the App
Section titled “Serving the App”To serve your new React app, run:
npx nx serve demoThe app is served at http://localhost:4200.
Nx uses the following syntax to run tasks:
Inferred Tasks
Section titled “Inferred Tasks”By default Nx simply runs your package.json scripts. However, you can also adopt Nx technology plugins that help abstract away some of the lower-level config and have Nx manage that. One such thing is to automatically identify tasks that can be run for your project from tooling configuration files such as package.json scripts and vite.config.ts.
In nx.json there's already the @nx/vite plugin registered which automatically identifies build, test, serve, and other Vite-related targets.
{ ... "plugins": [ { "plugin": "@nx/vite/plugin", "options": { "buildTargetName": "build", "testTargetName": "test", "serveTargetName": "serve", "devTargetName": "dev", "previewTargetName": "preview", "serveStaticTargetName": "serve-static", "typecheckTargetName": "typecheck", "buildDepsTargetName": "build-deps", "watchDepsTargetName": "watch-deps" } } ]}To view the tasks that Nx has detected, look in the Nx Console project detail view or run:
npx nx show project demo@acme/demo
Root: apps/demo
Type:application
Targets
build
vite build
Cacheable
If you expand the build task, you can see that it was created by the @nx/vite plugin by analyzing your vite.config.ts file. Notice the outputs are defined as {projectRoot}/dist. This value is being read from the build.outDir defined in your vite.config.ts file. Let's change that value in your vite.config.ts file:
export default defineConfig({ // ... build: { outDir: './build', // ... },});Now if you look at the project details view, the outputs for the build target will say {projectRoot}/build. The @nx/vite plugin ensures that tasks and their options, such as outputs, are automatically and correctly configured.
Modularization with Local Libraries
Section titled “Modularization with Local Libraries”When you develop your React application, usually all your logic sits in the app's src folder. Ideally separated by various folder names which represent your domains or features. As your app grows, however, the app becomes more and more monolithic, which makes building and testing it harder and slower.
Directoryacme/
Directoryapps/
Directorydemo/
Directorysrc/
Directoryapp/
- …
Directorycart/
- …
Directoryproducts/
- …
Directoryorders/
- …
Directoryui/
- …
Nx allows you to separate this logic into "local libraries." The main benefits include
- better separation of concerns
- better reusability
- more explicit private and public boundaries (APIs) between domains and features
- better scalability in CI by enabling independent test/lint/build commands for each library
- better scalability in your teams by allowing different teams to work on separate libraries
Create Local Libraries
Section titled “Create Local Libraries”Let's create a reusable design system library called ui that we can use across our workspace. This library will contain reusable components such as buttons, inputs, and other UI elements.
npx nx g @nx/react:library packages/ui --unitTestRunner=vitest --bundler=noneNote how we type out the full path in the directory flag to place the library into a subfolder. You can choose whatever folder structure you like to organize your projects.
Running the above commands should lead to the following directory structure:
Directoryacme/
Directoryapps/
Directorydemo/
- …
Directorypackages/
Directoryui/
- …
- eslint.config.mjs
- nx.json
- package-lock.json
- package.json
- tsconfig.base.json
- tsconfig.json
- vitest.workspace.ts
Just as with the demo app, Nx automatically infers the tasks for the ui library from its configuration files. You can view them by running:
npx nx show project uiIn this case, we have the lint and test tasks available, among other inferred tasks.
npx nx lint uinpx nx test uiImport Libraries into the Demo App
Section titled “Import Libraries into the Demo App”All libraries that we generate are automatically included in the workspaces defined in the root-level package.json.
{ "workspaces": ["apps/*", "packages/*"]}Hence, we can easily import them into other libraries and our React application.
You can see that the AcmeUi component is exported via the index.ts file of our ui library so that other projects in the repository can use it. This is our public API with the rest of the workspace and is enforced by the exports field in the package.json file. Only export what's necessary to be usable outside the library itself.
export * from './lib/ui';Let's add a simple Hero component that we can use in our demo app.
export function Hero(props: { title: string; subtitle: string; cta: string; onCtaClick?: () => void;}) { return ( <div style={{ backgroundColor: '#1a1a2e', color: 'white', padding: '100px 20px', textAlign: 'center', }} > <h1 style={{ fontSize: '48px', marginBottom: '16px', }} > {props.title} </h1> <p style={{ fontSize: '20px', marginBottom: '32px', }} > {props.subtitle} </p> <button onClick={props.onCtaClick} style={{ backgroundColor: '#0066ff', color: 'white', border: 'none', padding: '12px 24px', fontSize: '18px', borderRadius: '4px', cursor: 'pointer', }} > {props.cta} </button> </div> );} );}">




