Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Configure and optimize Turborepo monorepo build pipelines with correct task structure, caching, and CI setup.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/best-practices/packages.md
1# Creating Internal Packages23How to create and structure internal packages in your monorepo.45## Package Creation Checklist671. Create directory in `packages/`82. Add `package.json` with name and exports93. Add source code in `src/`104. Add `tsconfig.json` if using TypeScript115. Install as dependency in consuming packages126. Run package manager install to update lockfile1314## Package Compilation Strategies1516### Just-in-Time (JIT)1718Export TypeScript directly. The consuming app's bundler compiles it.1920```json21// packages/ui/package.json22{23"name": "@repo/ui",24"exports": {25"./button": "./src/button.tsx",26"./card": "./src/card.tsx"27},28"scripts": {29"lint": "eslint .",30"check-types": "tsc --noEmit"31}32}33```3435**When to use:**3637- Apps use modern bundlers (Turbopack, webpack, Vite)38- You want minimal configuration39- Build times are acceptable without caching4041**Limitations:**4243- No Turborepo cache for the package itself44- Consumer must support TypeScript compilation45- Can't use TypeScript `paths` (use Node.js subpath imports instead)4647### Compiled4849Package handles its own compilation.5051```json52// packages/ui/package.json53{54"name": "@repo/ui",55"exports": {56"./button": {57"types": "./src/button.tsx",58"default": "./dist/button.js"59}60},61"scripts": {62"build": "tsc",63"dev": "tsc --watch"64}65}66```6768```json69// packages/ui/tsconfig.json70{71"extends": "@repo/typescript-config/library.json",72"compilerOptions": {73"outDir": "dist",74"rootDir": "src"75},76"include": ["src"],77"exclude": ["node_modules", "dist"]78}79```8081**When to use:**8283- You want Turborepo to cache builds84- Package will be used by non-bundler tools85- You need maximum compatibility8687**Remember:** Add `dist/**` to turbo.json outputs!8889## Defining Exports9091### Multiple Entrypoints9293```json94{95"exports": {96".": "./src/index.ts", // @repo/ui97"./button": "./src/button.tsx", // @repo/ui/button98"./card": "./src/card.tsx", // @repo/ui/card99"./hooks": "./src/hooks/index.ts" // @repo/ui/hooks100}101}102```103104### Conditional Exports (Compiled)105106```json107{108"exports": {109"./button": {110"types": "./src/button.tsx",111"import": "./dist/button.mjs",112"require": "./dist/button.cjs",113"default": "./dist/button.js"114}115}116}117```118119## Installing Internal Packages120121### Add to Consuming Package122123```json124// apps/web/package.json125{126"dependencies": {127"@repo/ui": "workspace:*" // pnpm/bun128// "@repo/ui": "*" // npm/yarn129}130}131```132133### Run Install134135```bash136pnpm install # Updates lockfile with new dependency137```138139### Import and Use140141```typescript142// apps/web/src/page.tsx143import { Button } from '@repo/ui/button';144145export default function Page() {146return <Button>Click me</Button>;147}148```149150## One Purpose Per Package151152### Good Examples153154```155packages/156├── ui/ # Shared UI components157├── utils/ # General utilities158├── auth/ # Authentication logic159├── database/ # Database client/schemas160├── eslint-config/ # ESLint configuration161├── typescript-config/ # TypeScript configuration162└── api-client/ # Generated API client163```164165### Avoid Mega-Packages166167```168// BAD: One package for everything169packages/170└── shared/171├── components/172├── utils/173├── hooks/174├── types/175└── api/176177// GOOD: Separate by purpose178packages/179├── ui/ # Components180├── utils/ # Utilities181├── hooks/ # React hooks182├── types/ # Shared TypeScript types183└── api-client/ # API utilities184```185186## Config Packages187188### TypeScript Config189190```json191// packages/typescript-config/package.json192{193"name": "@repo/typescript-config",194"exports": {195"./base.json": "./base.json",196"./nextjs.json": "./nextjs.json",197"./library.json": "./library.json"198}199}200```201202### ESLint Config203204```json205// packages/eslint-config/package.json206{207"name": "@repo/eslint-config",208"exports": {209"./base": "./base.js",210"./next": "./next.js"211},212"dependencies": {213"eslint": "^8.0.0",214"eslint-config-next": "latest"215}216}217```218219## Common Mistakes220221### Forgetting to Export222223```json224// BAD: No exports defined225{226"name": "@repo/ui"227}228229// GOOD: Clear exports230{231"name": "@repo/ui",232"exports": {233"./button": "./src/button.tsx"234}235}236```237238### Wrong Workspace Syntax239240```json241// pnpm/bun242{ "@repo/ui": "workspace:*" } // Correct243244// npm/yarn245{ "@repo/ui": "*" } // Correct246{ "@repo/ui": "workspace:*" } // Wrong for npm/yarn!247```248249### Missing from turbo.json Outputs250251```json252// Package builds to dist/, but turbo.json doesn't know253{254"tasks": {255"build": {256"outputs": [".next/**"] // Missing dist/**!257}258}259}260261// Correct262{263"tasks": {264"build": {265"outputs": [".next/**", "dist/**"]266}267}268}269```270271## TypeScript Best Practices272273### Use Node.js Subpath Imports (Not `paths`)274275TypeScript `compilerOptions.paths` breaks with JIT packages. Use Node.js subpath imports instead (TypeScript 5.4+).276277**JIT Package:**278279```json280// packages/ui/package.json281{282"imports": {283"#*": "./src/*"284}285}286```287288```typescript289// packages/ui/button.tsx290import { MY_STRING } from "#utils.ts"; // Uses .ts extension291```292293**Compiled Package:**294295```json296// packages/ui/package.json297{298"imports": {299"#*": "./dist/*"300}301}302```303304```typescript305// packages/ui/button.tsx306import { MY_STRING } from "#utils.js"; // Uses .js extension307```308309### Use `tsc` for Internal Packages310311For internal packages, prefer `tsc` over bundlers. Bundlers can mangle code before it reaches your app's bundler, causing hard-to-debug issues.312313### Enable Go-to-Definition314315For Compiled Packages, enable declaration maps:316317```json318// tsconfig.json319{320"compilerOptions": {321"declaration": true,322"declarationMap": true323}324}325```326327This creates `.d.ts` and `.d.ts.map` files for IDE navigation.328329### No Root tsconfig.json Needed330331Each package should have its own `tsconfig.json`. A root one causes all tasks to miss cache when changed. Only use root `tsconfig.json` for non-package scripts.332333### Avoid TypeScript Project References334335They add complexity and another caching layer. Turborepo handles dependencies better.336