---
title: Structuring a repository
description: Start by creating a repository using the conventions of the ecosystem.
---

import { Callout } from '#components/callout';
import { PackageManagerTabs, Tab, Tabs } from '#components/tabs';
import { Step, Steps } from '#components/steps';
import { File, Folder, Files } from '#components/files';
import { LinkToDocumentation } from '#components/link-to-documentation';

`turbo` is built on top of [Workspaces](https://vercel.com/docs/vercel-platform/glossary#workspace), a feature of package managers in the JavaScript ecosystem that allows you to group multiple packages in one repository.

Following these conventions is important because it allows you to:

- Lean on those conventions for all your repo's tooling
- Quickly, incrementally adopt Turborepo into an existing repository

In this guide, we'll walk through setting up a multi-package workspace (monorepo) so we can set the groundwork for `turbo`.

## Getting started

Setting up a workspace's structure can be tedious to do by hand. If you're new to monorepos, we recommend [using `create-turbo` to get started](/docs/getting-started/installation) with a valid workspace structure right away.

<PackageManagerTabs>

<Tab value="pnpm">
```bash title="Terminal"
pnpm dlx create-turbo@latest
```

</Tab>

<Tab value="yarn">
```bash title="Terminal"
yarn dlx create-turbo@latest
```

</Tab>

<Tab value="npm">
```bash title="Terminal"
npx create-turbo@latest
```

</Tab>

<Tab value="bun (Beta)">
```bash title="Terminal"
bunx create-turbo@latest
```

</Tab>

</PackageManagerTabs>

You can then review the repository for the characteristics described in this guide.

## Anatomy of a workspace

In JavaScript, a workspace can either be [a single package](/docs/guides/single-package-workspaces) or a collection of packages. In these guides, we'll be focusing on [a multi-package workspace](https://vercel.com/docs/vercel-platform/glossary#monorepo), often called a "monorepo".

Below, the structural elements of `create-turbo` that make it a valid workspace are highlighted.

<PackageManagerTabs>

<Tab value="pnpm">
  <Files>
    <File name="package.json" green />
    <File name="pnpm-lock.yaml" green />
    <File name="pnpm-workspace.yaml" green />
    <File name="turbo.json" />
    <Folder name="apps" defaultOpen>
      <Folder name="docs" className="text-foreground" defaultOpen>
        <File name="package.json" green />
      </Folder>
      <Folder name="web">
        <File name="package.json" green />
      </Folder>
    </Folder>
    <Folder name="packages">
      <Folder name="ui">
        <File name="package.json" green />
      </Folder>
    </Folder>
  </Files>
</Tab>

<Tab value="yarn">
  <Files>
    <File name="package.json" green />
    <File name="yarn.lock" green />
    <File name="turbo.json" />
    <Folder name="apps" defaultOpen>
      <Folder name="docs" className="text-foreground" defaultOpen>
        <File name="package.json" green />
      </Folder>
      <Folder name="web">
        <File name="package.json" green />
      </Folder>
    </Folder>
    <Folder name="packages">
      <Folder name="ui">
        <File name="package.json" green />
      </Folder>
    </Folder>
  </Files>
</Tab>

<Tab value="npm">
  <Files>
    <File name="package.json" green />
    <File name="package-lock.json" green />
    <File name="turbo.json" />
    <Folder name="apps" defaultOpen>
      <Folder name="docs" className="text-foreground" defaultOpen>
        <File name="package.json" green />
      </Folder>
      <Folder name="web">
        <File name="package.json" green />
      </Folder>
    </Folder>
    <Folder name="packages">
      <Folder name="ui">
        <File name="package.json" green />
      </Folder>
    </Folder>
  </Files>
</Tab>

<Tab value="bun (Beta)">
  <Files>
    <File name="package.json" green />
    <File name="bun.lock" green />
    <File name="turbo.json" />
    <Folder name="apps" defaultOpen>
      <Folder name="docs" className="text-foreground" defaultOpen>
        <File name="package.json" green />
      </Folder>
      <Folder name="web">
        <File name="package.json" green />
      </Folder>
    </Folder>
    <Folder name="packages">
      <Folder name="ui">
        <File name="package.json" green />
      </Folder>
    </Folder>
  </Files>
</Tab>

</PackageManagerTabs>

### Minimum requirements

- [Packages as described by your package manager](#specifying-packages-in-a-monorepo)
- [A package manager lockfile](#package-manager-lockfile)
- [Root `package.json`](#root-packagejson)
- [Root `turbo.json`](#root-turbojson)
- [`package.json` in each package](#packagejson-in-each-package)

### Specifying packages in a monorepo

<Steps>
<Step>

#### Declaring directories for packages

First, your package manager needs to describe the locations of your packages. We recommend starting with splitting your packages into `apps/` for applications and services and `packages/` for everything else, like libraries and tooling.

<PackageManagerTabs>

  <Tab value="pnpm">
  ```json title="pnpm-workspace.yaml"
 packages:
    - "apps/*"
    - "packages/*"
  ```
  <LinkToDocumentation href="https://pnpm.io/pnpm-workspace_yaml">pnpm workspace documentation</LinkToDocumentation>

  </Tab>

  <Tab value="yarn">
  ```json title="./package.json"
  {
    "workspaces": [
      "apps/*",
      "packages/*"
    ]
  }
  ```

  <LinkToDocumentation href="https://yarnpkg.com/features/workspaces#how-are-workspaces-declared">yarn workspace documentation</LinkToDocumentation>
   </Tab>

  <Tab value="npm">
  ```json title="./package.json"
  {
    "workspaces": [
      "apps/*",
      "packages/*"
    ]
  }
  ```

  <LinkToDocumentation href="https://docs.npmjs.com/cli/v7/using-npm/workspaces#defining-workspaces">npm workspace documentation</LinkToDocumentation>
  </Tab>

  <Tab value="bun (Beta)">
  ```json title="./package.json"
  {
    "workspaces": [
      "apps/*",
      "packages/*"
    ]
  }
  ```

  <LinkToDocumentation href="https://bun.sh/docs/install/workspaces">bun workspace documentation</LinkToDocumentation>
  </Tab>

</PackageManagerTabs>

Using this configuration, every directory **with a `package.json`** in the `apps` or `packages` directories will be considered a package.

<Callout type="error">
Turborepo does not support nested packages like `apps/**` or `packages/**` due to ambiguous behavior among package managers in the JavaScript ecosystem. Using a structure that would put a package at `apps/a` and another at `apps/a/b` will result in an error.

If you'd like to group packages by directory, you can do this using globs like `packages/*` and `packages/group/*` and **not** creating a `packages/group/package.json` file.

</Callout>
</Step>

<Step>

#### `package.json` in each package

In the directory of the package, there must be a `package.json` to make the package discoverable to your package manager and `turbo`. The [requirements for the `package.json` of a package](#anatomy-of-a-package) are below.

</Step>
</Steps>

### Root `package.json`

The root `package.json` is the base for your workspace. Below is a common example of what you would find in a root `package.json`:

<PackageManagerTabs>

<Tab value="pnpm">

```json title="./package.json"
{
  "private": true,
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "lint": "turbo run lint"
  },
  "devDependencies": {
    "turbo": "latest"
  },
  "packageManager": "pnpm@9.0.0"
}
```

</Tab>

<Tab value="yarn">

```json title="./package.json"
{
  "private": true,
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "lint": "turbo run lint"
  },
  "devDependencies": {
    "turbo": "latest"
  },
  "packageManager": "yarn@1.22.19",
  "workspaces": ["apps/*", "packages/*"]
}
```

</Tab>

<Tab value="npm">

```json title="./package.json"
{
  "private": true,
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "lint": "turbo run lint"
  },
  "devDependencies": {
    "turbo": "latest"
  },
  "packageManager": "npm@10.0.0",
  "workspaces": ["apps/*", "packages/*"]
}
```

</Tab>

<Tab value="bun (Beta)">

```json title="./package.json"
{
  "private": true,
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "lint": "turbo run lint"
  },
  "devDependencies": {
    "turbo": "latest"
  },
  "packageManager": "bun@1.2.0",
  "workspaces": ["apps/*", "packages/*"]
}
```

</Tab>
</PackageManagerTabs>

### Root `turbo.json`

`turbo.json` is used to configure the behavior of `turbo`. To learn more about how to configure your tasks, visit the [Configuring tasks](/docs/crafting-your-repository/configuring-tasks) page.

### Package manager lockfile

A lockfile is key to reproducible behavior for both your package manager and `turbo`. Additionally, Turborepo uses the lockfile to understand the dependencies between your [Internal Packages](/docs/core-concepts/internal-packages) within your Workspace.

<Callout type="warn">
  If you do not have a lockfile present when you run `turbo`, you may see
  unpredictable behavior.
</Callout>

## Anatomy of a package

It's often best to start thinking about designing a package as its own unit within the Workspace. At a high-level, each package is almost like its own small "project", with its own `package.json`, tooling configuration, and source code. There are limits to this idea—but its a good mental model to _start_ from.

Additionally, a package has specific entrypoints that other packages in your Workspace can use to access the package, specified by [`exports`](#exports).

### `package.json` for a package

#### `name`

[The `name` field](https://nodejs.org/api/packages.html#name) is used to identify the package. It should be unique within your workspace.

<Callout type="info">
It's best practice to use a namespace prefix for your [Internal Packages](/docs/core-concepts/internal-packages) to avoid conflicts with other packages on the npm registry. For example, if your organization is named `acme`, you might name your packages `@acme/package-name`.

We use `@repo` in our docs and examples because it is an unused, unclaimable namespace on the npm registry. You can choose to keep it or use your own prefix.

</Callout>

#### `scripts`

The `scripts` field is used to define scripts that can be run in the package's context. Turborepo will use the name of these scripts to identify what scripts to run (if any) in a package. We talk more about these scripts on the [Running Tasks](/docs/crafting-your-repository/running-tasks) page.

#### `exports`

[The `exports` field](https://nodejs.org/api/packages.html#exports) is used to specify the entrypoints for other packages that want to use the package. When you want to use code from one package in another package, you'll import from that entrypoint.

For example, if you had a `@repo/math` package, you might have the following `exports` field:

```json title="./packages/math/package.json"
{
  "exports": {
    ".": "./src/constants.ts",
    "./add": "./src/add.ts",
    "./subtract": "./src/subtract.ts"
  }
}
```

Note that this example uses the [Just-in-Time Package](/docs/core-concepts/internal-packages#just-in-time-packages) pattern for simplicity. It exports TypeScript directly, but you might choose to use the [Compiled Package](/docs/core-concepts/internal-packages#compiled-packages) pattern instead.

<Callout type="info">
  The `exports` field in this example requires modern versions of Node.js and
  TypeScript.
</Callout>

This would allow you to import `add` and `subtract` functions from the `@repo/math` package like so:

```ts title="./apps/my-app/src/index.ts"
import { GRAVITATIONAL_CONSTANT, SPEED_OF_LIGHT } from '@repo/math';
import { add } from '@repo/math/add';
import { subtract } from '@repo/math/subtract';
```

Using exports this way provides three major benefits:

- **Avoiding barrel files**: Barrel files are files that re-export other files in the same package, creating one entrypoint for the entire package. While they might appear convenient, they're [difficult for compilers and bundlers to handle](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js#what's-the-problem-with-barrel-files) and can quickly lead to performance problems.
- **More powerful features**: `exports` also has other powerful features compared to [the `main` field](https://nodejs.org/api/packages.html#main) like [Conditional Exports](https://nodejs.org/api/packages.html#conditional-exports). In general, we recommend using `exports` over `main` whenever possible as it is the more modern option.
- **IDE autocompletion**: By specifying the entrypoints for your package using `exports`, you can ensure that your code editor can provide auto-completion for the package's exports.

#### `imports` (optional)

[The `imports` field](https://nodejs.org/api/packages.html#imports) gives you a way to create subpaths to other modules within your package. You can think of these like "shortcuts" to write simpler import paths that are more resilient to refactors that move files. To learn how, visit [the TypeScript page](/docs/guides/tools/typescript#use-nodejs-subpath-imports-instead-of-typescript-compiler-paths).

<Callout type="info">
You may be more familiar with TypeScript's `compilerOptions#paths` option, which accomplishes a similar goal. As of TypeScript 5.4, TypeScript can infer subpaths from `imports`, making it a better option since you'll be working with Node.js conventions. For more information, visit [our TypeScript guide](/docs/guides/tools/typescript#use-nodejs-subpath-imports-instead-of-typescript-compiler-paths).

</Callout>

### Source code

Of course, you'll want some source code in your package. Packages commonly use an `src` directory to store their source code and compile to a `dist` directory (that should also be located within the package), although this is not a requirement.

## Common pitfalls

- If you're using TypeScript, you likely don't need a `tsconfig.json` in the root of your workspace. Packages should independently specify their own configurations, usually building off of a shared `tsconfig.json` from a separate package in the workspace. For more information, visit [the TypeScript guide](/docs/guides/tools/typescript#you-likely-dont-need-a-tsconfigjson-file-in-the-root-of-your-project).
- You want to avoid accessing files across package boundaries as much as possible. If you ever find yourself writing `../` to get from one package to another, you likely have an opportunity to re-think your approach by installing the package where it's needed and importing it into your code.

## Next steps

With your Workspace configured, you can now use your package manager to [install dependencies into your packages](/docs/crafting-your-repository/managing-dependencies).
