# How to Manage TypeScript Configurations in a Monorepo: A Complete Guide

> Master TypeScript configurations in your monorepo. Learn how the Tech Interview Handbook centralizes settings with a reusable tsconfig package for efficient project management and overrides.

- Repository: [Yangshun Tay/tech-interview-handbook](https://github.com/yangshun/tech-interview-handbook)
- Tags: how-to-guide
- Published: 2026-02-25

---

**The Tech Interview Handbook monorepo centralizes TypeScript configurations through a dedicated `@tih/tsconfig` package that provides reusable base, Next.js, and React library presets, allowing each workspace to extend shared defaults while maintaining project-specific overrides.**

Managing TypeScript configurations in a monorepo requires a balance between consistency across workspaces and flexibility for individual projects. The yangshun/tech-interview-handbook repository demonstrates an effective centralized strategy using pnpm workspaces and a dedicated configuration package to manage TypeScript configurations in a monorepo without sacrificing local customization.

## Centralized TypeScript Configuration Strategy

The repository employs a **single source of truth** approach. Instead of duplicating compiler options across dozens of [`tsconfig.json`](https://github.com/yangshun/tech-interview-handbook/blob/main/tsconfig.json) files, the team maintains a central `@tih/tsconfig` package under `packages/tsconfig/`. This package exports three distinct presets tailored to different project types, ensuring that strict mode, module resolution, and JSX handling remain consistent while allowing apps to layer their own path aliases and output directories.

## Workspace Layout and Structure

The monorepo organizes code into two primary directories:

- `apps/` – Individual applications (e.g., the portal frontend)
- `packages/` – Shared libraries and configuration packages, including the `tsconfig` package

### The pnpm Workspace Configuration

The workspace boundaries are defined in [`pnpm-workspace.yaml`](https://github.com/yangshun/tech-interview-handbook/blob/main/pnpm-workspace.yaml) at the repository root:

```yaml
packages:
  - 'apps/*'
  - 'packages/*'

```

This configuration instructs pnpm to treat every subdirectory under `apps/` and `packages/` as an independent package that can depend on other workspaces via the `workspace:` protocol.

## The Shared @tih/tsconfig Package

Located at `packages/tsconfig/`, this package contains three reusable configuration files. Each preset extends the one above it, creating a layered inheritance model.

### Base Configuration (base.json)

The [`base.json`](https://github.com/yangshun/tech-interview-handbook/blob/main/base.json) file provides strict, framework-agnostic defaults suitable for any TypeScript project:

```json
{
  "$schema": "https://json.schemastore.org/tsconfig",
  "display": "Default",
  "compilerOptions": {
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "exclude": ["node_modules"]
}

```

### Next.js Configuration (nextjs.json)

The [`nextjs.json`](https://github.com/yangshun/tech-interview-handbook/blob/main/nextjs.json) preset extends the base and adds settings optimized for Next.js applications, including JSX preservation and path alias support:

```json
{
  "extends": "./base.json",
  "display": "Next.js",
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "jsx": "preserve",
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "incremental": true,
    "plugins": [{ "name": "next" }]
  },
  "include": ["src", "next-env.d.ts"],
  "exclude": ["node_modules"]
}

```

### React Library Configuration (react-library.json)

For reusable React component libraries, the [`react-library.json`](https://github.com/yangshun/tech-interview-handbook/blob/main/react-library.json) provides a minimal setup with the React JSX transform:

```json
{
  "extends": "./base.json",
  "display": "React Library",
  "compilerOptions": {
    "jsx": "react-jsx",
    "lib": ["ES2015", "DOM"],
    "module": "ESNext",
    "target": "ES6"
  }
}

```

## Extending Shared Configs in Applications

Individual workspaces consume these presets by extending them in their local [`tsconfig.json`](https://github.com/yangshun/tech-interview-handbook/blob/main/tsconfig.json) files and overriding specific `compilerOptions` as needed.

### Example: Portal App Configuration

The portal application at [`apps/portal/tsconfig.json`](https://github.com/yangshun/tech-interview-handbook/blob/main/apps/portal/tsconfig.json) demonstrates how to layer project-specific settings on top of the shared Next.js preset:

```json
{
  "exclude": ["node_modules"],
  "extends": "@tih/tsconfig/nextjs.json",
  "compilerOptions": {
    "rootDir": "src",
    "outDir": "dist",
    "baseUrl": "./src",
    "paths": {
      "~/*": ["*"]
    }
  },
  "ts-node": {
    "transpileOnly": true,
    "compilerOptions": {
      "module": "CommonJS"
    }
  },
  "include": ["src", "next-env.d.ts"]
}

```

Key implementation details:

- **`extends`** imports the shared Next.js defaults from `@tih/tsconfig/nextjs.json`.
- **`paths`** maps `~/` to the `src/` directory, enabling clean absolute imports like `~/components/Button`.
- **`ts-node`** overrides the module system to `CommonJS` for running database seed scripts with Prisma.

## Benefits of This Architecture

This centralized approach to managing TypeScript configurations in a monorepo delivers several operational advantages:

- **Single source of truth** – Updating [`packages/tsconfig/base.json`](https://github.com/yangshun/tech-interview-handbook/blob/main/packages/tsconfig/base.json) instantly propagates to every workspace that extends it, eliminating configuration drift.
- **Fast iteration** – Because `@tih/tsconfig` is referenced via the `workspace:` protocol (e.g., `"@tih/tsconfig": "workspace:0.0.0"` in [`apps/portal/package.json`](https://github.com/yangshun/tech-interview-handbook/blob/main/apps/portal/package.json)), pnpm symlinks the files directly. Changes are immediate without npm publishes or version bumps.
- **Consistent tooling** – ESLint, Prettier, and CI pipelines can rely on uniform TypeScript settings for type-checking and linting across all packages.

## Adding New Workspaces

To onboard a new package or application while maintaining the centralized configuration strategy:

1. **Create the directory** under `apps/` or `packages/`.
2. **Add a [`tsconfig.json`](https://github.com/yangshun/tech-interview-handbook/blob/main/tsconfig.json)** that extends the appropriate shared preset (e.g., `@tih/tsconfig/react-library.json` for UI components).
3. **Configure local overrides** such as `outDir`, `rootDir`, or custom path aliases in `compilerOptions`.
4. **Reference the workspace** in [`pnpm-workspace.yaml`](https://github.com/yangshun/tech-interview-handbook/blob/main/pnpm-workspace.yaml) (the wildcard pattern `apps/*` and `packages/*` automatically includes new folders).

## Practical Implementation Examples

### Creating a React Component Library

For a new shared UI library at `packages/ui`, extend the React library preset and enable declaration files for consumers:

```json
{
  "extends": "@tih/tsconfig/react-library.json",
  "compilerOptions": {
    "outDir": "dist",
    "declaration": true,
    "declarationMap": true,
    "paths": {
      "@ui/*": ["src/*"]
    }
  },
  "include": ["src"]
}

```

Applications can then import components using the workspace protocol in their [`package.json`](https://github.com/yangshun/tech-interview-handbook/blob/main/package.json):

```json
{
  "dependencies": {
    "@ui": "workspace:*"
  }
}

```

### Configuring Cross-Workspace Path Aliases

To share a common utility package across all workspaces, update the shared [`base.json`](https://github.com/yangshun/tech-interview-handbook/blob/main/base.json) to include a global path alias:

```json
{
  "$schema": "https://json.schemastore.org/tsconfig",
  "display": "Default",
  "compilerOptions": {
    "strict": true,
    "noEmit": true,
    "baseUrl": ".",
    "paths": {
      "@common/*": ["packages/common/src/*"]
    }
  },
  "exclude": ["node_modules"]
}

```

Any workspace extending this config can now resolve `@common/utils` to `packages/common/src/utils` without additional local configuration.

### Running TypeScript Scripts with ts-node

For database seeding or one-off scripts that require TypeScript execution, configure the `ts-node` section in your app's [`tsconfig.json`](https://github.com/yangshun/tech-interview-handbook/blob/main/tsconfig.json) to override module settings:

```json
{
  "extends": "@tih/tsconfig/nextjs.json",
  "compilerOptions": {
    "rootDir": "src"
  },
  "ts-node": {
    "transpileOnly": true,
    "compilerOptions": {
      "module": "CommonJS"
    }
  }
}

```

Execute the script using pnpm from the workspace root:

```bash
pnpm -C apps/portal ts-node prisma/seed.ts

```

This ensures the script runs with `CommonJS` module resolution while the main application uses `ESNext`.

## Summary

- **Centralize configurations** by creating a dedicated `packages/tsconfig` package with presets for different project types (base, Next.js, React libraries).
- **Extend, don't duplicate** – Each workspace references shared configs via `"extends": "@tih/tsconfig/nextjs.json"` and adds only project-specific overrides.
- **Leverage workspace protocols** – Reference `@tih/tsconfig` using `"workspace:0.0.0"` in `devDependencies` for instant updates without publishing.
- **Standardize path aliases** – Define common aliases in the shared base config or locally per app to maintain clean import semantics across the monorepo.

## Frequently Asked Questions

### How do I add a custom path alias to a specific app in the monorepo?

Extend the shared configuration in your app's [`tsconfig.json`](https://github.com/yangshun/tech-interview-handbook/blob/main/tsconfig.json) and add a `paths` entry under `compilerOptions`. For example, to map `~/` to your `src/` directory:

```json
{
  "extends": "@tih/tsconfig/nextjs.json",
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "~/*": ["*"]
    }
  }
}

```

This keeps the shared defaults intact while giving the specific workspace its own import shortcuts.

### Why use a workspace package instead of publishing @tih/tsconfig to npm?

Using the `workspace:` protocol (e.g., `"@tih/tsconfig": "workspace:0.0.0"`) allows changes to the shared configs to propagate immediately to all consuming packages without version bumps, publishing delays, or network requests. Since the monorepo owns all the code, this internal dependency never needs to be published externally, enabling faster iteration and ensuring all workspaces always use the latest compiler settings.

### How does the base configuration enforce strict type checking across all projects?

The [`packages/tsconfig/base.json`](https://github.com/yangshun/tech-interview-handbook/blob/main/packages/tsconfig/base.json) file sets `"strict": true` and `"noEmit": true` at the root level. Because every other preset (Next.js, React library) extends this base file using `"extends": "./base.json"`, these strict settings inherit automatically. Any workspace that then extends `@tih/tsconfig/nextjs.json` or `@tih/tsconfig/react-library.json` receives the strict defaults unless explicitly overridden, ensuring consistent type safety standards across the entire monorepo.

### Can I override specific compiler options from the shared config?

Yes. The [`tsconfig.json`](https://github.com/yangshun/tech-interview-handbook/blob/main/tsconfig.json) inheritance model allows child configurations to override any parent setting. For example, if the shared base sets `"target": "ES6"` but your specific app requires `"target": "ES2020"`, simply declare the new value in your local `compilerOptions`. The local setting takes precedence while all other unspecified options remain inherited from the shared preset.