How to run a Remix app + package with turborepo

Felipe Freitag Vargas - Jul 11 '22 - - Dev Community

Developing remix-forms was cumbersome because it wasn't connected directly to a Remix 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 for the end result or go to the sample monorepo to see a working configuration without any business logic.

For this article, I'll use a new Netlify Remix app and an empty TS package.

Strategy

We considered three options:

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
Enter fullscreen mode Exit fullscreen mode

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 guide, we got a couple of config files.

// ./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
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

And a root package.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"
  }
}
Enter fullscreen mode Exit fullscreen mode

Now wire the app to use the local sample-package:

// apps/web/package.json
{
  "name": "remix-web-app",
  //...
  "dependencies": {
    //...
    "sample-package": "*",
    //...
  }
  //...
}
Enter fullscreen mode Exit fullscreen mode

It's already possible to see it working 🎉

// root dir
$ npm i
$ npm run dev
Enter fullscreen mode Exit fullscreen mode

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!

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

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 😁

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.

$ npm run build
$ cd apps/packages/sample-package
$ npm version <major|minor|patch>
$ npm publish
Enter fullscreen mode Exit fullscreen mode

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.

// apps/web/netlify.toml
[build]
  command = "cd ../.. && npm install && npm run build"
  ...
Enter fullscreen mode Exit fullscreen mode

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.

. . . . . . . . . . . . . . . . . . . . . . . . .