How to Set Up a MonoRepo with TurboRepo: A Step-by-Step Guide
What is a Monorepo?
-
Monorepo stands for "mono-repository."
-
It's a single repository that holds the code for multiple projects.
-
Imagine having all your apps, libraries, and tools in one big folder!
Example:
-
A company has a web app, a mobile app, and shared libraries.
-
Instead of separate repositories, they keep everything in one monorepo.
Advantages and Disadvantages of Monorepo
| Advantages | Disadvantages |
|---|---|
| Easier code sharing and reuse | Can become large and complex |
| Simplified dependency management | Longer build times |
| Consistent tooling and configuration | Requires more robust CI/CD pipelines |
| Easier refactoring across projects | Potential for merge conflicts |
Common Interview Questions and Answers
-
What is a monorepo?
- A single repository containing multiple projects.
-
Why use a monorepo?
- For easier code sharing and consistent tooling.
-
What is Turborepo?
- A tool to manage monorepos efficiently.
-
How does Turborepo improve monorepos?
- By optimising build times and simplifying workflows.
What is Turborepo and Why It's Useful
-
Turborepo is a high-performance build system for JavaScript and TypeScript monorepos.
-
It speeds up builds by caching and running tasks in parallel.
-
Useful for large teams and projects to maintain efficiency.
Run the command to set up your turbo repo project
-
Install Turborepo:
bashnpx create-turbo@latestThe turbo repo's folder structure. The apps folder contains multiple apps that you can add or remove.
-

All the Ui of the app will be created inside the UI directory, which is inside the package folder
-

To Import Ui components from the ui directory to all your apps.
-
We have to use
import componentname "@repo/ui/componentname" syntax to import any ui component from the ui package.
javascript"use client" import TextInput from "@repo/ui/TextInput" import Button from "@repo/ui/button" import { useRouter } from "next/navigation" import { useState } from "react" export default function Home() { const [roomId, setroomId] = useState("") const router = useRouter() function handleChange(e: any) { setroomId(e.target.value) } function handleSubmit() { if (roomId.trim() == "") { alert("Please Enter Room id") return } else { router.push(`/chat/room/${roomId}`) } } return ( <div style={{ display: "flex", justifyContent: "center", alignItems: "center", width: "99vw", height: "100vh", background: "black", color: "white" }}> <div style={{display:"flex",flexDirection:"column",gap:"10px"}}> <TextInput onChange={handleChange} placeholder="Enter Room Id" /> <Button onClick={handleSubmit}>Join Meeting</Button> </div> </div> ) }
Adding the Common ts-Config file for all the backends
Step 1. Create a common json file in the package folder inside the typescript-config file
Step 2. Copy your typescript configuration.

Step 3. Now, whenever you use the backend, you don't need to add the full TypeScript configuration. You can simply extend it and provide the path to your config file that you created in the typescript-config file.
json{ "extends":"@repo/typescript-config/backend.json" }

Step 4. Also specify the rootdir and outdir with the compiler option in each backend app inside the tsconfig.json file

About the Turbo.json File
json{ "$schema": "https://turborepo.com/schema.json", "ui": "tui", "tasks": { "build": { "dependsOn": ["^build"], "inputs": ["$TURBO_DEFAULT$", ".env*"], "outputs": [".next/**", "!.next/cache/**"] }, "lint": { "dependsOn": ["^lint"] }, "check-types": { "dependsOn": ["^check-types"] }, "dev": { "cache": false, "persistent": true } } }
The provided JSON snippet is a configuration file for Turborepo, which is used to manage tasks and dependencies in a monorepo setup. Here's a breakdown of each line and its meaning:
-
"$schema": "https://turborepo.com/schema.json": This line specifies the schema for the JSON file, which helps validate the configuration's structure and content. -
"ui": "tui": This line sets the user interface for Turborepo to "tui" (text user interface), which might be a custom or specific UI configuration. -
"tasks": This section defines various tasks that Turborepo can execute, such as build, lint, check-types, and dev.-
"build":-
"dependsOn": ["^build"]: This indicates that the build task depends on the build tasks of all dependencies. The caret (^) symbol is used to denote dependency tasks. -
"inputs": ["$TURBO_DEFAULT$", ".env*"]: Specifies the inputs for the build task.$TURBO_DEFAULT$is a placeholder for default inputs, and.env*includes any environment files. -
"outputs": [".next/**", "!.next/cache/**"]: Defines the outputs of the build task. It includes everything in the.nextdirectory except for the cache.
-
-
"lint":"dependsOn": ["^lint"]: Similar to the build task, this indicates that the lint task depends on the lint tasks of all dependencies.
-
"check-types":"dependsOn": ["^check-types"]: This indicates that the check-types task depends on the check-types tasks of all dependencies.
-
"dev":-
"cache": false: This setting disables caching for the dev task, meaning it will run fresh each time. -
"persistent": true: This indicates that the dev task should run persistently, likely for development purposes where continuous watching is needed.
-
-
To configure Turborepo so that changes in the server folder or independent files trigger a rebuild of the dist directory, you can modify the inputs section of the relevant task (e.g., build) to include the server folder or specific files. For example:
json"inputs": ["$TURBO_DEFAULT$", ".env*", "server/**", "independent-file.js"]
This configuration will ensure that any changes in the server directory or independent-file.js will trigger the build process, updating the dist directory accordingly.
How to Configure Backend Rebuilds to Respond to Changes and Manage Caching
When we run the npm run build command, all the packages are built and cached. If we make changes to the Node.js application code and run the build command again, it uses the cached version instead of building a fresh app. To force a fresh build of the backend, we need to modify the configuration.
Step 1. Create a turbo.json file inside the backend application.

Step 2. Then extend the code and specify the output to also include the dist folder.
json{ "extends": ["//"], "task": { "build": { "outputs": ["dist/**"] } } }
In this case, when we run the npm run build command globally, it forces Turborepo to build a fresh backend app and not use the cached version if there are changes in the backend files.
If we want to include any custom files in the fresh build, we can specify them globally.
json"build": { "dependsOn": ["^build"], "inputs": ["$TURBO_DEFAULT$", ".env*", "server/**", "independent-file.js"], "outputs": ["dist/**"] }