Your first Turborepo
Tags: javascript, react, webdev, monorepo
Published: 2022-04-29 20:42:22

## Introduction

Monorepos are fantastic. They let you maintain all your projects in a single repository. I use one at my workplace and I see its advantages everyday. If you know anything about monorepos, setting them up can be tricky sometimes. Recently, I've been following the developments over at [Turborepo](https://turborepo.org/), which attempts to make setting up the tooling for monorepose simpler. The more I look through their docs, the more I get exited about using it. So, I gave it a shot and I have to say, the experience has been fantastic.

## Why this article?

If you're wondering you can just go to their docs and set it up yourself, yes, you absolutely can. They have a cli which can help you setup a new project and they have a solid set of [examples](https://github.com/vercel/turborepo/tree/main/examples) for most scenarios. But, it is super fun setting things up from scratch, and I wanted to see how much of work it is with Turborepo.

## The setup

I'll setting up a new monorepo with a couple of simple apps and a UI library which would be shared by the apps. The goal is not the design and functionalities of these apps, but the tooling and features Turborepo provides. There will be two apps `admin` and `products`, both of them will be bundled using [Vite](https://vitejs.dev/). Vite is blazing fast and you should definitely give it a try just for its speed. The UI library, which will contain just a button component, which is written in TypeScript, will be bundled using [tsup](https://tsup.egoist.sh/). `tsup` uses `esbuild` underneath, so we can expect blazing fast build times. I'll be using yarn for package management. We will also be using a common `eslint` configuration which will be shared across all three codebases.

Let's get started!

## Turborepo stuff

Let's first create a folder for our project and start initialising our monorepo.
As with any JS project, we start with a `package.json`.

`package.json`

![package.json](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0jk23re27riic7i31ykl.png) 

This is the initial config I'm using. It has `turbo` and `eslint` installed as a devDependency. If you are familiar with monorepos, the `workspaces` array should make sense. All the projects in your monorepo should be listed as a workspace. Here, we have two directories, `apps` contain `admin` and `products`, and `packages`, which contains the UI library and the eslint configuration. Anything that can be shared across multiple projects can live in the `packages` folder.

Next is our `turbo.json`. This is Turborepo's config file. I browsed through their examples and found the simplest config to get started.

`turbo.json`

![turbo.json](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/olr5dk21y29trh8ngwbg.png)

We'll be covering this in a later section.

## Setting up apps 

Vite has a cli which makes it easier for us to bootstrap a React app.
In our `apps` folder, run

```
yarn create vite admin --template react
```
This will create a new react app named `admin`. Similarly, we can create `products` app as well.

```
yarn create vite products --template react
```

Now we have two apps named `admin` and `products` in our `apps` directory.

## Setting up the library
I've added all the dependencies needed for a TS library with types and eslint packages. Also added are the scripts for `build`, `dev`, `lint`.

`packages/ui/package.json`

![packages/ui/package.json](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6u2h4msn579hgn1gljyp.png)
 

Now, lets simply add a `Button` component and export it.

`packages/ui/Button.tsx`

![packages/ui/Button.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oqlrh899aeg46s2ox7h7.png)

`packages/ui/index.tsx`

![packages/ui/index.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggz6qnrltup31inh50j2.png)

Now, our project looks like this

![project structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qpv9lsqfns5acsy3lyh2.png)

Now that we have setup our apps and library, we can setup the tooling to link(_turbocharge_) them.

## Add library as a dependency

The next step is to add the library as a dependency to our apps. It is as simple as adding it to `devDependecies` in both `apps/admin/package.json` and `apps/products/package.json`.

![apps/admin/package.json](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hyzfyxahzswddlkb2ilk.png)

Turborepo will use the `name` field in the library's `package.json` to resolve it in the apps.

We can now use this `Button` component in `admin` and products.

In `apps/admin/src/App.jsx`

![apps/admin/src/App.jsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2m6mhyghpivgh9u47pnb.png)

We can do the same thing in `apps/products/src/App.jsx` as well.

## Adding scripts

The final step before we test this is to add scripts for `build`, `lint` and `dev`. In our root `package.json`, we can add

![package.json](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o75anh1l8dp223i8a0ue.png)

These commands are directly tied to the `pipeline` configurations in `turbo.json`. For example, if we look at the `build` command, with the `"dependsOn": ["^build"],` option, we are letting Turborepo know that build commands should only be run after all its dependencies are built. Turborepo is smart enough to realise `admin` has a dependency `ui`, which needs to be built before building `admin`. So, it builds `ui` first and then bundle `admin`. `Pipelines` are a powerful feature in Turborepo and you can read about it [here](https://turborepo.org/docs/core-concepts/pipelines).

Now, there is nothing left but to run our two apps. First, we would need to install our dependencies by running,
```
yarn install
```

Then, we start the dev server using
```
yarn dev
```

If we inspect the terminal messages, we can see that `admin` is running in `localhost:3000` and `products` is running in `localhost:3001`.

![Console](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lgmnrb42daoeuiol1jdw.png)
 
(Look at the insane 2.914s start times! Vite FTW!)

Now if we navigate to `localhost:3000`, we see

![localhost:3000](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uivvp2cdf8m52d299hkl.png)

We can see our button component is rendering as expected.

## Setting up shared lint config

Similar to how we shared a library across apps, we can share config files across apps as well. We'll be using a single `eslint` config in all our apps and library. For that we shall create a folder called `config` in our `packages` directory.
Inside it, we'll create a file `eslint-preset.js`,

![eslint-preset.js](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kopz2ybyk1gshslfua9c.png)

And a `package.json`.

`packages/config/package.json`

![packages/config/package.json](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mlv3wbzrj55en0d14elc.png)

The package.json contains all the `eslint` packages we'll be needing, and notice the `files` property includes the lint config file.

Now, we add `config` as a dev dependency in `admin`, `products` and `ui`. In each of their `package.json`, add it as a `devDependency`

`apps/admin/package.json`

![apps/admin/package.json](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b4ttby16lje55uc24v0y.png)

Also, we would need a `.eslintrc.js` which simply exports the lint config from `config`.

`apps/admin/.eslintrc.js`

![apps/admin/.eslintrc.js](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/39rnvirximhfw5p9zg9i.png)
  
Now, we we run `yarn lint` on our root folder, Turborepo will run the lint command on all of our projects.

Notice that we did not need to install `eslint`(except in the root) or its corresponding packages anywhere else other than the `config` folder.

Awesome! We have setup our own monorepo with two apps, a library and a shared eslint config.


## Conclusion

This idea of monorepos can be extended and even backend code can be added to the same repo. One awesome use-case I can think of is sharing types between frontend and backend apps using a shared package. We have barely scratched the surface of Turborepo and its features. [Remote Caching](https://turborepo.org/docs/core-concepts/remote-caching) 
  is one such feature I'm exited to try out. Meanwhile, this exercise was a great starting point.

The source code for this can be found [here](https://github.com/sidv93/turborepo-test)

Cheers!