# How to successfully set up a NextJS monorepo and deploy it on Vercel

For people that are new to monorepos like me, there are a lot of struggles that you will face and no one talks about them in the articles you can find out there, especially when it comes to monorepos with NextJS apps.

## Setup a monorepo with NPM or Yarn

You can setup monorepos in various ways: with `npm`, `yarn`, `pnpm`,  `nx`, `lerna`, `bit.dev`, etc. but since my project doesn't need to maintain/publish packages to the npm repository, I chose to go simple and try with npm and yarn.

To add monorepo support in npm or yarn, you simply have to add a `workspaces` entry to your `package.json` file in the root directory of your monorepo project, like this: 

```json
{
  "private": true,
  "workspaces": [
    "packages/*"
  ]
}
```

Initially, I wanted to have `apps/*` and `packages/*` separately, but that was just making my tooling & scripts more complicated, unnecessarily. So I decided to put everything (NextJS apps and shared packages) under `packages/*`, for simplicity.

Now here comes the tricky part, because I had a very hard time understanding how could I reuse my packages (written in TypeScript and CSS modules) in my NextJS apps.

The `npm` workspaces documentation is very deficient and doesn't tell anything about how to import your packages. So I thought I could just include them as a dependency, using local package resolution:
```
{
   "dependencies": {
      "@myorg/designsystem": "file:../designsystem"
   }
}
```

Then you run `npm install` and your local package is hard-linked in the `node_modules` folder of the package that requires it. 

With `yarn`, instead of the file resolution, you would use `workspace:*`.

The difference between yarn and npm when it comes to workspaces is that yarn is more efficient: it keeps a single `node_modules` folder in the root, instead of one per package (like npm does). Yarns also prune the dependencies automatically.

The only problem with having a single node_modules is when you or some of your dependencies require a different version of React. When some of your packages need a different version of a dependency that is shared with other of your packages, you end up with the same package in the root node_modules and in your package's node_modules folder that needs a particular version.

This can cause problems, especially in NextJS apps, which don't like to have another version of React in the parent `node_modules`, so you need to take special care of that.

So, after understanding how dependencies were working for each package manager, then the problem came when I tried to import the source files (in TS) of my packages. The compiler will complain that there is no loader configured for that (sorry, what??).

Yes, I had to compile my packages to JS before they could be used by the others. Somehow I felt this wasn't the right approach and that it wasn't going to work for me, as it was making my developer experience very bad and frustrating.

If you try to import TypeScript files directly, that won't work, unless you enable NextJS experimental features to include external files and configure your `tsconfig.json` files to include all packages TS/TSX.
I tried that and it felt ugly and hacky. CSS Modules don't work with this approach.

## The official Vercel approach for monorepos

I was about to surrender and abandon the idea of having a monorepo with my related NextJS apps,
and then [I found this official Vercel example](https://github.com/vercel/next.js/blob/189fc985a709bf67986ef58502c0c599abdb2ff2/examples/with-yarn-workspaces) of how to configure workspaces with `yarn`.

It saved my life. You only need to configure your workspaces with yarn, and use the [`next-transpile-modules` NextJS plugin](https://github.com/vercel/next.js/blob/189fc985a709bf67986ef58502c0c599abdb2ff2/examples/with-yarn-workspaces/packages/web-app/next.config.js) to start being able to import your packages directly.

No manual compilation steps, no need to add it as a dependency, and no need for tsconfig.json changes! All works out of the box: JS, JSX, TS, TSX, including JSON and CSS Modules.

This is the configuration I used for my NextJS apps:

```js
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true
}

const sharedPackages = ["../designsystem", "../core"]

const withTM = require("next-transpile-modules")(sharedPackages) // monorepo support

// withTM needs to be the further-most plugin you wrap your config with.
module.exports = withTM(nextConfig)

```

After this, you can import your packages using their package name:

```tsx
import { Button } from "@myorg/designsystem/forms"
```

You only need to have a `index.ts` or `index.js` file in the root of every package (even if they just have an empty export), in order for the `next-transpile-modules` to work, but you can organize your code in folders, you don't need to export everything in the main index file.

I still need to figure out whether this will also work inside NextJS server-side scripts (e.g. importing node js/ts files of my packages in `pages/api/*` files), but for now this solution covers all the needs I had of reusing my components in different NextJS apps.

It works so seamlessly that every time you change your packages, NextJS will detect that and make a hot reload while you are developing, which makes the developer experience really great.


## Deploying the apps of your monorepo on Vercel

Once you are done, to deploy every NextJS separately in Vercel, you only need to configure the root directory in your Vercel app's settings. For example, if your app is under `packages/myapp`, this should be the value in the Vercel settings.

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1654989436681/WNn9r9boD.png align="left")

You also should set up the Git Ignore Build Step section, to ignore the build if the files of your app directory didn't change, by adding `git diff HEAD^ HEAD --quiet .`. 


![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1654989465482/wqSjaXhRd.png align="left")

Otherwise, new deployments will be triggered even if you changed files of other apps.
If you changed a component dependency that is under another package folder, then you will need to trigger the build manually or write a more advanced git check.

To know more, check this [guide on Vercel's website](https://vercel.com/guides/deploying-yarn-monorepos-to-vercel) and [this other article from them](https://vercel.com/blog/monorepos).

