Getting started with Turborepo

Getting started with Turborepo

Unlock the full potential of your codebase: Why Turborepo is the ideal choice for modern monorepos?

Ever feel like your monorepo builds are moving at a snail's pace? Are you tired of waiting minutes, or even hours, to see a simple change reflected in your project? Well, buckle up, developers! Because Turborepo is here to inject some serious speed into your workflow. This game-changing build system unlocks the power of parallel builds, allowing you to turbocharge your development process and leave those sluggish builds in the dust. But how does it work? And what kind of performance gains can you expect? In this post, we'll dive deep into Turborepo's parallelization magic and the motive of this article is to help the reader get started with the monorepo approach. ‍Readers are highly recommended to go on hands-on practice as they read the article to get the full essence of Turborepo.

Introduction

Turborepo is the secret weapon for taming large monorepos. It empowers you to build, manage, and collaborate on multi-project codebases with ease, unlocking significant improvements in speed, organization, and overall development efficiency.

Here are some key features provided by turbo repo:

  • Blazing-fast builds: Turborepo significantly accelerates build times using caching, parallel processing, incremental builds, and remote caching.

  • Declarative configuration: Easily define your build pipeline and dependencies with Turborepo's simple and clear configuration syntax.

  • Monorepo-friendly: Specifically designed to manage multi-project codebases within a single repository, promoting code sharing and collaboration.

  • Developer-friendly tools: Offers visualization tools, debugging, and profiling capabilities for smoother development workflows. Works seamlessly with popular JavaScript/TypeScript frameworks and libraries.

Installation

For creating a turborepo application just spin up the command

npx create-turbo@latest

After answering basic questions like your folder name ./my-project and package manager like (npm, yarn, pnpm, bun), it will create a default directory structure and install the necessary packages.

Exploration

It creates five major workspaces on your behalf: app/web, app/docs, packages/ui, packages/eslint-config, and packages/typescript-config, each of which has its package.json.

We will go through each of these workspaces one by one to get a better understanding of what's going on with each of them.

apps/web & apps/docs

It is a standalone next application that acts as one of the entry points for the application. Turborepo allows us to have multiple instances of these independent projects built under one hood. We can add numerous other projects with different frameworks like create-react-app, vue, vite, etc under the apps directory.

package/ui

One of the superpowers of mono repo is their ability to promote code sharing and consistency across multiple applications. Let's dive into a practical example of how this works within our Turborepo setup. We have defined our button component inside packages/ui/src/components/button

import React from 'react';

const Button = ({ onClick, label }) => {
  const buttonStyle = {
    padding: '10px 15px',
    fontSize: '16px',
    backgroundColor: '#007bff',
    color: '#fff',
    border: 'none',
    borderRadius: '5px',
    cursor: 'pointer',
    outline: 'none',
  };

  return (
    <button style={buttonStyle} onClick={onClick}>
      {label}
    </button>
  );
};

export default Button;

Now we can go in app/web project to use this component directly by importing it from @repo/ui as shown below:

import { Button } from "@repo/ui";
import Image from "next/image";
import Link from "next/link";


export default function Page() {
  return (
    <Button>Click Here</Button>
  )
}

Harnessing the Power of packages/ui

  • Navigate to the packages/ui folder, and you'll discover a package named @repo/ui inside package.json. This package holds a collection of reusable UI components that serve as the foundation for our design system.

  • Now, take a peek inside the package.json files for both the web and docs apps. Notice anything interesting? They both share a common dependency: @repo/ui.

That's right! These apps directly depend on our local shared component library. This elegant pattern demonstrates the essence of code sharing within a monorepo.

package/typescript-config

It enables shared typescript configurations throughout the entire repository.

In the typescript-config package's package.json:

{
  "name": "@repo/typescript-config"
}

Here, the package is named @repo/typescript-config.Now, let's explore the tsconfig.json file in our web app. In the apps/web/tsconfig.json:

{
  "extends": "@repo/typescript-config/nextjs.json"
}

As you can see, we're directly importing @repo/typescript-config/nextjs.json into our tsconfig.json file. This approach enables the sharing of a single tsconfig.json configuration across all workspaces in the monorepo, minimizing code duplication.

package/eslint-config

Our focus now turns to the eslint-config , the last workspace, a crucial element for maintaining standardized ESLint configurations. Beginning with a peek into the packages/eslint-config/package.json file, we discover the following structure:

{
  "name": "@repo/eslint-config",
  "files": [
    "library.js",
    "next.js",
    "react-internal.js"
  ]
}

To comprehend how custom ESLint configurations are seamlessly integrated, let's delve into the apps/docs/.eslintrc.js file:

module.exports = {
  extends: ["@repo/eslint-config/next.js"],
};

Much akin to the approach adopted by typescript-config, the eslint-config empowers us to share ESLint configurations uniformly throughout the monorepo. This cohesive strategy ensures a consistent linting experience, regardless of the specific project at hand.

Next Steps

We've reached the bittersweet end of this exploration of shared components within Turborepo. And as you embark on this exhilarating path, keep this in mind:

code should never be duplicated, only beautifully shared.

As a next step, we will try to understand how turborepo builds are so fast and how they cache previous results. Following this we will also look into how we can set up various styling libraries (.i.e tailwindcss, Material-UI), followed by CI-CD approaches for turboprops in an efficient way and what configuration options are available for us to personalize our monorepo.

Did you find this article valuable?

Support Shivam Akhouri by becoming a sponsor. Any amount is appreciated!