Powering Up React Development With Nx

Jack Hsu
Nx Devtools
Published in
11 min readMar 20, 2019

--

Jack Hsu is a software architect at nrwl.io, providing consulting to enterprise teams.

This blog post is the second about how Nx works with React. You can also read our introductory blog post by Victor Savkin: “Building Angular and React Applications Together With Nx.”

The most common way to start a React project is to use the official Create React App. There are a few other starter kits, and their main focus is on getting developers up and running quickly.

While the existing React starter kits are great tools, they don’t offer help on some common development tasks and problems. For example:

  • How to structure a project and promote best practices.
  • Generating new components and other files (e.g. Redux reducers, action creators, etc.).
  • Integrating with a unit test framework such as Jest.
  • Integrating with a end-to-end (E2E) test framework such as Cypress.

Some or all of the above are left as manual tasks for developers, depending on the tool you choose. And these problems, plus others, are what Nx (created by Nrwl) is built to solve.

What is Nx?

Nx is a tool maintained by us at Nrwl that helps manage workspaces in a monorepo way.

With Nx, you can:

  • Use modern tools like Cypress and Jest
  • Build full-stack applications and share code between the backend and the frontend
  • Use effective development practices pioneered at Google

Getting Started

Let’s create a new React workspace using one of the following three methods. If you’re not sure which to use, use npx.

npx

npx create-nx-workspace my-app

npm

npm init nx-workspace my-app

yarn

yarn create nx-workspace my-app

Make sure to choose the react framework when prompted. We’ll also be using SCSS in this example.

This command sets up the basic structure for our workspace inside the newly created my-app folder.

apps/
my-app/
my-app-e2e/
libs/
jest.config.js
package.json
nx.json
tsconfig.json
tslint.json
workspace.json
yarn.lock

You can run the app right away using yarn start, and opening http://localhost:4200 to see the result.

Next, let’s take a look at what we can do with Nx out of the box.

What Nx Provides Out of the Box

Besides providing a way to serve the app, here are other things Nx gives you with zero initial configuration.

Opinionated Code Formatting (Prettier)

We think Prettier is an awesome tool and every project should use it!

Nx provides two commands that leverage Prettier.

  1. yarn format:check — Checks if files are formatted correctly.
  2. yarn format:write— Updates files with correct formatting.

Again, we provide the default configuration file (.prettierrc), which you can modify to fit your needs.

Now that we know some of the functionalities that Nx affords us, let’s continue building our app.

TypeScript and Linting

Nx generates new workspaces with TypeScript as the default language.

TypeScript can improve the development experience by enabling editors to show code completion, hover info, and signature information through IntelliSense. These helpers will allow developers to write code more quickly and correctly.

In addition to adding TypeScript support, we also configure the workspace to provide static code analysis through linting. This analysis furthers ensures readability, maintainability, and correctness.

To lint the app, use the yarn lint command.

Common defaults for TypeScript and lint are provided in the tsconfig.json and tslint.json files at the project root, and you can change these to fit your own preferences.

Running Unit Tests (Jest)

To run unit tests, use the yarn test command.

We generate projects using Jest as the test runner since we believe it is the best solution for most projects. The default configurations are in jest.config.js, and you can customize this if needed.

Running E2E Tests (Cypress)

We generate projects using Cypress as the E2E testing framework since we believe it is the best choice for most projects.

To run E2E tests, use the yarn e2e --watch command, which will start the Cypress app. Once Cypress has started, you can run individual specs, or run all of them at once.

Note: The --watch flag is passed in the command above. This is useful during development since changes to source files will cause Cypress to update and rerun tests. For continuous integration environments you will want this flag off.

Adding a New Feature

Let’s make our app do something interesting, shall we?

Generating a Feature Library

Apps are composed of many features, so it’s a good idea to group them into separate libraries that will be imported by the main app.

We’ll start by adding a Home feature that provides some introductory text for our application.

yarn nx generate lib --name=home --no-interactive --framework=react

You should see something like this.

Pro-Tip: If we want to preview the changes before committing them, we can pass the --dry-run flag.

Now let’s update our Home component as follows.

Home component

Adding Routes to the Application

Let’s use React Router to handle the routing in our application.

yarn

yarn add react-router react-router-dom

Or npm

npm install react-router react-router-dom

Next, we’ll update our app component and styles to the following.

App component
App styles

Now, serve the application again yarn start and you should see the following.

Feature #2: GIF Search

Let’s add one more feature. Say we want to allow users to search against the Giphy API.

yarn nx generate lib --name=gifs --no-interactive --framework=react
yarn add axios # Install HTTP client

Let’s implement a Giphy search within our component:

Gifs component
Gifs styles

And to use the Gifs component from our app, let’s update app.tsx to the following.

Updated App component

Then, in our apps/my-app/src/environments/environment.ts file, let’s add the Giphy entry.

Environment config file

Now the app should look like this.

To use the application in production, we can run the following command.

ng build my-app --prod

This will generate assets under dist/apps/my-app that can then be served statically.

And that is how features are added in Nx. For the full implementation, please refer to the git repo.

To recap, so far we’ve seen how to:

  • Generate a new React monorepo workspace with one application.
  • Add new feature libraries and route to them from our application.

Schematics: Codifying Component Generation with Styled-Components

Update (July 30, 2019): Note that React component schematic includes styled-components as a built-in option as of Nx 8.4.0. Thus you don’t need this workspace schematic anymore, but it is still useful to know how to put one together.

Suppose that we want to use a CSS-in-JS solution to style our components instead of SCSS. In this workspace, let’s go with styled-components.

The usual process goes like this:

  1. Install styled-components as a dependency.
  2. Create a new file for the component manually.
  3. Create a test file for the component manually.
  4. Add an entry to the index.ts file of the module.

Each step of this process is tedious and prone to human error.

Instead, with Nx we can create a custom workspace schematic to automate this process.

yarn nx generate workspace-schematic react-component

This command will create a new workspace schematic that can be used for code generation. We won’t go into schematics in detail since they are out of scope for this post. However, here’s a look at the react-component schematic.

(For the full implementation, please refer to the git repo.)

React-component workspace schematic

Notice that we don’t have to run yarn add … or yarn because the schematic adds our dependencies and runs install automatically.

With workspace schematics under our belts we can start to codify and automate more of our development workflow. Here are some ideas for additional schematics:

In the next section, let’s examine how our workspace is organized, and the benefits of using Nx as the workspace scales out.

Monorepo-Style Development

When a workspace is generated using Nx, it is generated as a monorepo.

What is a monorepo you ask? The simple definition is that a monorepo is a single repository where multiple projects live, and they may depend on each other. This way of development is used at companies such as Facebook and Google, as well as popular open source projects such as Babel, React, and Jest.

But why should you use a monorepo? Some advantages include:

  • Less friction to sharing components and code since they are in the same repository.
  • Frontend and backend code are colocated, which makes feature development easier since they can happen in the same branch.
  • Don’t have to worry about publishing and versioning dependencies among different projects since they all exist together (in the same branch or version).

Monorepos and Nx

In Nx, we separate projects into applications and libraries. Think of projects as modules that are distinct but may have dependencies on each other.

An application is a composition of libraries that can run in the browser or server. They live in the apps folder.

A library on the other hand is a group of functionalities with a well-defined API. A library can depend on other libraries, and cannot run on their own. They live in the libs folder.

Note: The initial workspace has two apps by default: apps/my-app and apps/my-app-e2e. The former project is what we serve to end users, and the e2e project is what we will use to perform E2E testing on the app.

More than one app

Say you want to co-locate backend services in the same workspace. You can do this by generating a NestJS app as follows.

yarn nx generate node-application --name=api --framework=nestjs --no-interactive

This command will generate a new application named api inside the apps/api folder.

You can start the server right away using yarn start api, and visiting http://localhost:3333 to see the result.

Notice that we pass the app name to the start command because we now have more than one app. To start the React app we can still use yarn start(since it is the workspace default), or use yarn start my-appto be more explicit.

Understanding Our Dependencies

As our workspace continues to grow, it is crucial that we understand the dependencies between applications and libraries. This is why Nx provides the dep-graph command to illustrate the dependencies.

yarn dep-graph

This command will generate the following graph.

As you can see, the my-app project depends on the gifs and home libraries. Application nodes are rectangles, and library nodes are ovals. You can also see the api project has no dependencies nor dependents.

But let’s say that my-app actually depends on the api since it makes requests to it. We can modify nx.json to denote this.

In nx.json, under projects, add implicitDependencies for my-app.

nx.json

And if we rerun yarn dep-graph we’ll see the following.

Now that we’re happy with the setup, let’s commit our code to master.

git add .
git commit -m "added api project"

Testing and Building Only Affected Code

One of the challenges in working with a larger codebase is knowing what’s been affected from a given changeset.

The naive approach to testing software is to retest and rebuild everything whenever anything changes in the workspace. But this is inefficient and we can do better!

Fixing a Bug and Re-Testing Affected Code

You might have noticed that the GIF search feature has a bug in it. The request to Giphy API happens regardless of whether the user has typed in the search box.

This request is unnecessary, so let’s fix the component effect.

And to check what applications and libraries have been affected, we can use the following command.

yarn affected:dep-graph --base=master

This will show the dependency graph again, but this time with the affected nodes and paths highlighted in red. The --base=master flag (which refers to the git revision) gives Nx a reference point to check the changeset against.

Since gifs, my-app, and my-app-e2e are affected, we will need to rerun tests for them. Nx provides a handy shortcut for this.

yarn affected:test --base=master --parallel

This will run the tests for all affected projects in parallel. Neat!

Other affected commands Nx provides are:

  • affected:e2e — Runs E2E test for affected projects.
  • affected:lint — Lints affected projects.
  • affected:apps — Lists all affected apps.
  • affected:libs — Lists all affected libs.
  • affected:build — Builds all affected apps.

For more information on affected commands, please refer to our guide.

To recap, we learned:

  • How Nx structures our workspace into two categories of projects: applications and libraries.
  • We saw how we can visualize the dependency graph of our projects.
  • Lastly, we learned about affected commands and how they help to visualize affected projects, as well as provide shortcuts for running tests, lint, etc.

In Closing

In this post you learned how to create a new React workspace using Nx. Then, we codified our library and component generation by using workspace schematics. Lastly, we saw how the affected commands can help our workspace scale as it continues to grow in size.

Please check out the working repo if you want to play around with the examples in this post yourself.

The material covered in this post scratches only the surface of what Nx provides. If you’re interested in learning more, please head over to our docs!

Next steps for the workspace may include:

  • Bringing other apps into the workspace so they can easily share libraries (and be retested when needed).
  • Creating a workspace schematic to generate a Redux module (actions, reducers, etc.).
  • Extend the built-in webpack config by providing webpackConfig option for @nrwl/builders:web-build and @nrwl/builders:web-dev-server builders in workspace.json.

I hope you find this post useful, and I’d love to hear some feedback if you decide to check out Nx!

Further Info

Jack Hsu is a software Architect at Nrwl .

If you liked this, click the 👏 below so other people will see this here on Medium. Follow Jack and Nrwl_io to read more about Nx, Angular and React.

--

--

Software Architect at Nrwl (nrwl.io), providing consulting to enterprise teams.