Deploying a Monorepo Using Yarn Workspaces to Vercel

In this guide, you will deploy a monorepo that includes two frontend applications and one shared library with Yarn workspaces.

Migrating from one framework to another can sometimes be a laborious experience, especially if you have a chunk of functionality that needs to be used in both applications during the migration process.

With a large codebase it may take a while to complete an incremental migration. Monorepos allow you to move portions of your application piece-by-piece, and continue to deploy the application under one domain and from a single code repository.

In this guide you will deploy a monorepo using Yarn workspaces and the following components:

  • A create-react-app application
  • A Next.js application
  • A common library containing a utility function shared between each application

Structure

Before diving in, let's look at the monorepo structure we will create:

Each of the 2 folders in apps will be Yarn workspaces and any folder in the packages folder will also be a workspace. Each workspace will have its own package.json file with dependencies and everything will be installed and linked in the root node_modules when the yarn command is run. For the purpose of this guide, the term workspace will be used to describe each of these folders.

Process

  1. Set up the monorepo root with Yarn
  2. Create a workspace for a Create React App application
  3. Create a workspace for a Next.js application
  4. Create a shared library workspace with a utility function
  5. Use the function inside both applications
  6. Connect all the pieces with Yarn workspaces
  7. Test the monorepo locally
  8. Deploy to Vercel
  9. Configure the Ignored Build Step on Vercel
Note: What follows is a step by step guide that shows you how to create everything from scratch. If you would like to cut to the chase and get the Deployments up and running so that you can experiment with the code, feel free to clone the Git repository and follow the instructions on the README.md file to deploy.

Create a monorepo using Yarn workspaces

Set up the monorepo root with Yarn

Create your monorepo folder from the command line and initialize it.
mkdir yarn-monorepo
cd yarn-monorepo
yarn init

Choose all the default values for each prompt, except for private which you should mark as true. The default is false and Yarn workspaces only work with private projects. This will create a package.json file that looks like this:

{
  "name": "yarn-monorepo",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "private": true
}

Create a workspace for a Create React App application

Create the apps folder to contain the workspaces for all applications.

mkdir apps
cd apps

Create the frontend workspace by running the following command inside yarn-monorepo/apps:

yarn create react-app frontend

This creates a create-react-app application inside the frontend folder.

Create a workspace for a Next.js application

From the folder, yarn-monorepo/apps, run:

yarn create next-app nextapp

This creates a Next.js application inside the nextapp folder.

Create a shared library workspace with a utility function

Create a new packages directory for code that will be shared within the monorepo. After that, create a new site-info folder in the packages directory and initialize it with yarn. You can create any number of packages here to share code amongst multiple applications.

mkdir packages
cd packages
mkdir site-info
cd site-info
yarn init
Note: You can use the default values for the answers to all prompts.

You will now create a simple function that you can use in both the create-react-app and the Next.js applications. This function will output the site title and subtitle.

Create an index.js file inside yarn-monorepo/packages/site-info with the following content:

const PROJECT = {
  title: 'Site Title',
  subtitle: 'My great monorepo',
};

export function getSiteInfo() {
  return { title: PROJECT.title, subtitle: PROJECT.subtitle };
}

Use the function inside both applications

Replace the content of yarn-monorepo/apps/nextapp/pages/index.js with:

import Head from 'next/head';
import styles from '../styles/Home.module.css';

// Import the shared function into the Next.js application
import { getSiteInfo } from 'site-info';

export default function Home() {
  let siteInfo = getSiteInfo(); //Define a variable to get the values
  return (
    <div className={styles.container}>
      <Head>
        <title>{siteInfo.title}</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      {/*Output the site title and subtitle to the screen*/}
      <main className={styles.main}>
        <h1 className={styles.title}>Welcome to {siteInfo.title}</h1>
        <p className={styles.description}>{siteInfo.subtitle}</p>
      </main>
    </div>
  );
}

Replace the content of the yarn-monorepo/apps/frontend/src/App.js file with:

import './App.css';

// Import the shared function into the `create-react-app` application
import { getSiteInfo } from 'site-info';

export default function App() {
  //Define a variable to get the values
  let siteInfo = getSiteInfo();
  return (
    <div className="App">
      {/*Output the site title and subtitle to the screen*/}
      <header className="App-header">
        <h1>{siteInfo.title}</h1>
        <p>{siteInfo.subtitle}</p>
      </header>
    </div>
  );
}

Connect all the pieces with Yarn workspaces

Add the following to the package.json file in the root of yarn-monorepoto tell Yarn about your workspaces:

  "workspaces": [
    "apps/*",
    "packages/*"
  ]
Note: This configures 3 workspaces in your project. Each workspace is first located by path, but addressed by the `name` value in their respective `package.json` files. In this example, the project has the following workspaces:
  • apps/frontend: A create-react-app application
  • apps/nextapp: A Next.js application to which the frontend application is being migrated
  • packages: A folder containing reusable packages for sharing code between the applications

From the repository root directory, yarn-monorepo, run the following in order to install and connect all packages:

yarn

Test the monorepo locally

Add the following content to the package.json file at the root of yarn-monorepo:

  "scripts": {
    "start": "yarn --cwd apps/frontend start",
    "next": "yarn --cwd apps/nextapp dev",
    "dev": "npm-run-all --parallel start next"
  }

Add the npm-run-all development dependency by running:

yarn add --dev npm-run-all

Run the following at the root of yarn-monorepo:

yarn
Note: This installs the package npm-run-all so that you can run multiple projects in parallel locally. This is not needed in production.

Then, run the following command to start both the frontend and the nextapp applications locally:

yarn dev
Note: This runs the command npm-run-all --parallel start next which runs yarn --cwd frontend start (create-react-app application) and yarn --cwd nextapp dev (Next.js application) at the same time. You can now browse to http://localhost:3000 and http://localhost:3001 to see each application being served separately from the same repository.

The browser output for localhost:3000 and localhost:3001

Deploy to Vercel

You will now link a Vercel Project to each frontend site that you would like to have a Deployment for. In this case, it will be for:

  • the frontend folder
  • the nextapp folder

Let's deploy by using git.

First we need to remove the git repository from the frontend and nextapp folders, as this comes by default when setting up those frameworks and with a monorepo you only need one repository for all the workspaces.

Run the following command in both the yarn-monorepo/frontend and yarn-monorepo/nextapp folders:

rm -rf .git

Set up git from the root yarn-monorepo by running:

git init

Add a .gitignore file at the root:

touch .gitignore

Paste the following content:

node_modules

Inside your Git hosting provider account that is connected to Vercel, create a git repository.

Connect it to your yarn-monorepo folder using by running git remote add origin [yourGitRepoURL] in the root folder and push your changes:

git checkout -b main
git add .
git commit -m "first commit"
git push origin main

Deploy both applications to Vercel from this same repository by creating a new Vercel Project with Git twice and selecting the repository from the Import Git Repository section. Use the following configuration for each Vercel Project:

  • For the apps/frontend create-react-app application, choose Create React App as the framework and apps/frontend as the root directory
  • For the apps/nextapp Next.js application, choose Next.js as the framework and apps/nextapp as the root directory

Once each Project is deployed, you will see 2 sites that look like the following:

Configure the Ignored Build Step on Vercel

Since you have 2 Vercel Projects connected to the same repository, a git push with a change to any part of the repository will cause a Deployment to be triggered in both Projects.

It would be better for a Deployment to be triggered in the create-react-app Project only when a change happens in apps/frontend and similarly for the Nextjs Project, when a change happens in apps/nextapp. This is possible with Vercel by configuring the Ignored Build Step option inside the Git section of the Project Settings.

For each Project, set the Ignored Build Step to git diff HEAD^ HEAD --quiet . and click Save.

The reason why you set the directory that you would like this command to return true on to ., referring to the root folder, is because you have configured the root of the Project to be the application's folder such as apps/frontend or apps/nextapp.

That's it!

In this guide, you completed the following tasks:

  • Created a monorepo with a workspace for your applications under apps and a workspace for your shared code under packages
  • Deployed the monorepo on Vercel as 2 Projects with their own Deployment URL
  • Configured the Ignored Build Step setting on each Project so that Deployments are triggered in each Project only when changes in the Project's folder happen

To learn more, please review the following links:



Written By
Written by ismaelrumzanismaelrumzan