How to run a Remix app + package with turborepo
Tags: remix, turborepo, javascript, node
Published: 2022-07-11 18:24:18

Developing [remix-forms](https://github.com/SeasonedSoftware/remix-forms) was cumbersome because it wasn't connected directly to a [Remix](https://remix.run) app using it. Testing the initial iterations involved publishing the package and importing it on a separate test web app. It was quick and dirty, and it worked when we had a couple of people writing it.

It was better to have everything in a single place in order to reduce development friction going forward.


## Goals

This is a very simple use case that didn't need many features.

- Develop the site using the local version of remix-forms
- Hot reload the site when the package code changes
- Deploy the site easily
- Keep CI simple, running the e2e tests that we already have

Spoilers: check [remix-forms](https://github.com/SeasonedSoftware/remix-forms) for the end result or go to the [sample monorepo](https://github.com/SeasonedSoftware/sample-package-monorepo) to see a working configuration without any business logic.

For this article, I'll use a new [Netlify Remix app](https://github.com/SeasonedSoftware/sample-package-monorepo/tree/main/apps/web) and an empty [TS package](https://github.com/SeasonedSoftware/sample-package-monorepo/tree/main/packages/sample-package).


## Strategy

We considered three options:
- [Turborepo](https://turborepo.org/)
- [Nx](https://nx.dev/)
- [NPM workspaces](https://docs.npmjs.com/cli/v8/using-npm/workspaces)

It seemed NPM workspaces would work and we wouldn't need any other dependencies. But there were some quirks to make the web app load the local package. After some trial and error, we decided to try the external tools.

Turborepo was pretty simple to setup and the fastest of the three from installation to seeing it working.

Nx docs weren't as easy to follow. We tried it for maybe half an hour, and decided to go with the one that "just worked". Again, our use case isn't complex and there isn't a need for tons of features.

Turborepo was the tool for this job.


## Preparing the file structure

```
.
turbo.json
package.json
`-- packages
   +-- sample-package
`-- apps
   +-- web
```

First, we created a new root dir and copied the contents of the package to `/packages` and the Remix app to `/apps/web`.


## Configure Turborepo
Following [turborepo's](https://turborepo.org/docs/getting-started#add-turborepo-to-your-existing-monorepo) guide, we got a couple of config files.

```json
// ./turbo.json
{
  "$schema": "https://turborepo.org/schema.json",
  "baseBranch": "origin/main",
  "pipeline": {
    "build": {
      "dependsOn": [
        "^build"
      ],
      "outputs": [
        "dist/**"
      ]
    },
    "lint": {
      "outputs": []
    },
    "test": {
      "outputs": [],
      "dependsOn": [
        "^build"
      ]
    },
    "dev": {
      "cache": false
    }
  }
}
```

And a root `package.json`: 

```json
// ./package.json
{
  "name": "sample-monorepo",
  //...
  "workspaces": [
    "packages/*",
    "apps/*"
  ],
  "devDependencies": {
    "turbo": "^1.3.1"
  },
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "lint": "turbo run lint",
    "test": "turbo run test",
    "test:ci": "turbo run test:ci"
  }
}
```

Now wire the app to use the local sample-package:
```json
// apps/web/package.json
{
  "name": "remix-web-app",
  //...
  "dependencies": {
    //...
    "sample-package": "*",
    //...
  }
  //...
}
```

It's already possible to see it working :tada:

```
// root dir
$ npm i
$ npm run dev
```


## Reload Remix app when the package changes

But Remix only rebuilds when the `apps/web` folder changes, not when the package does.
Enter the brand new `config.watchPaths` from Remix [1.6.4!](https://github.com/remix-run/remix/releases/tag/v1.6.4)

```js
// apps/web/remix.config.js
/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
  //...
  watchPaths: ['../../packages/sample-package'],
  //...
};
```

Now we run everything with a single command `npm run dev` at the root dir, and any changes to the package will trigger a Remix rebuild :grin:


## Build

Run `npm run build` at the root dir and it's done.


## Deploy

We didn't change the publishing process for the package yet. 

```sh
$ npm run build
$ cd apps/packages/sample-package
$ npm version <major|minor|patch>
$ npm publish
```

In order to deploy the web app to Netlify, we need one extra configuration on `apps/web/nelify.toml`. The rest of the file is the default generated by Remix.

```toml
// apps/web/netlify.toml
[build]
  command = "cd ../.. && npm install && npm run build"
  ...
```

We're done! Our basic workflow is much simpler. Clone the repo, install dependencies and everything is ready to run. It's much easier to have more people handling the code of the package.
Merge a PR to main and the website updates, no need for extra steps.

There's certainly room for improvement as we're just scratching the surface of what this structure can do. But for now, that's the job we needed to be done.