monorepo-patterns
Skill2 starsChanged 6 months agoMITView on GitHub
Workspace architecture patterns for Turborepo, Nx, and pnpm workspaces. Scalable multi-package project structures with shared configs, optimized builds, and dependency management.
- Reads credentials
- Deletes or force-pushes
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 247
Not setting outputs in turbo.json — breaks remote caching
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Monorepo Patterns
Workspace architecture patterns for Turborepo, Nx, and pnpm workspaces. Scalable multi-package project structures with shared configs, optimized builds, and dependency management.
Workspace Structure
monorepo/
├── apps/
│ ├── web/ # Next.js app
│ ├── mobile/ # React Native app
│ └── docs/ # Documentation site
├── packages/
│ ├── ui/ # Shared component library
│ ├── config/ # Shared configs (ESLint, TS, Tailwind)
│ ├── utils/ # Shared utilities
│ └── types/ # Shared TypeScript types
├── turbo.json # Turborepo pipeline config
├── pnpm-workspace.yaml # Workspace definition
└── package.json # Root package.jsonTurborepo Pipeline Configuration
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**", "dist/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
},
"typecheck": {
"dependsOn": ["^build"]
}
}
}Shared Package Pattern
// packages/ui/package.json
{
"name": "@workspace/ui",
"version": "0.0.0",
"private": true,
"exports": {
"./button": "./src/button.tsx",
"./card": "./src/card.tsx",
"./input": "./src/input.tsx",
"./styles.css": "./src/styles.css"
},
"devDependencies": {
"@workspace/config": "workspace:*",
"typescript": "^5.0.0"
}
}
// packages/ui/src/button.tsx
import { forwardRef, type ButtonHTMLAttributes } from 'react';
import { cn } from '@workspace/utils';
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'ghost';
size?: 'sm' | 'md' | 'lg';
}
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant = 'primary', size = 'md', ...props }, ref) => {
return (
<button
ref={ref}
className={cn(
'inline-flex items-center justify-center rounded-md font-medium transition-colors',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
'disabled:pointer-events-none disabled:opacity-50',
variantStyles[variant],
sizeStyles[size],
className
)}
{...props}
/>
);
}
);
Button.displayName = 'Button';
const variantStyles = {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200',
ghost: 'hover:bg-gray-100 text-gray-700',
} as const;
const sizeStyles = {
sm: 'h-8 px-3 text-sm',
md: 'h-10 px-4 text-sm',
lg: 'h-12 px-6 text-base',
} as const;Shared Config Pattern
// packages/config/eslint/index.js
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'prettier',
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'import'],
rules: {
'import/order': ['error', {
groups: ['builtin', 'external', 'internal', 'parent', 'sibling'],
'newlines-between': 'always',
alphabetize: { order: 'asc' },
}],
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'@typescript-eslint/consistent-type-imports': 'error',
},
};
// packages/config/tsconfig/react.json
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ES2020",
"lib": ["DOM", "DOM.Iterable", "ES2020"],
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
}
}Internal Package Consumption
// apps/web/package.json — consuming shared packages
{
"name": "@workspace/web",
"dependencies": {
"@workspace/ui": "workspace:*",
"@workspace/utils": "workspace:*",
"@workspace/types": "workspace:*"
}
}
// apps/web/next.config.js — transpile internal packages
const nextConfig = {
transpilePackages: ['@workspace/ui', '@workspace/utils'],
};
module.exports = nextConfig;
// apps/web/src/app/page.tsx
import { Button } from '@workspace/ui/button';
import { formatDate } from '@workspace/utils';
import type { User } from '@workspace/types';Dependency Management
# pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'// Root package.json scripts
{
"scripts": {
"build": "turbo build",
"dev": "turbo dev",
"lint": "turbo lint",
"test": "turbo test",
"typecheck": "turbo typecheck",
"clean": "turbo clean && rm -rf node_modules",
"format": "prettier --write \"**/*.{ts,tsx,md}\""
}
}Versioning and Changesets
// .changeset/config.json
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [["@workspace/ui", "@workspace/utils"]],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["@workspace/web", "@workspace/docs"]
}Checklist
- Workspace packages use
workspace:*protocol for internal deps - Turborepo pipeline has correct
dependsOngraph - Build outputs are specified for caching
- Shared configs are consumed via package references, not copy-paste
transpilePackagesconfigured in Next.js for internal packages- TypeScript project references or paths configured
- CI runs
turbo build --filter=...[origin/main]for affected-only builds - Changesets configured for versioning public packages
- Root
.gitignorecovers all app/package build outputs devtasks marked aspersistent: trueandcache: false
Anti-Patterns
- Duplicating configs across apps instead of sharing from
packages/config - Using
*orlatestinstead ofworkspace:*for internal dependencies - Missing
dependsOn: ["^build"]causing stale package builds - Putting app-specific code in shared packages
- Circular dependencies between workspace packages
- Not setting
outputsin turbo.json — breaks remote caching - Hardcoding absolute paths instead of using workspace aliases
In this repository18 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- accessibilitySkill
- api-layerSkill
- auth-patternsSkill
- core-web-vitalsSkill
- css-architectureSkill
- design-intelligenceSkill
- error-handlingSkill
- frontend-designSkill
- i18nSkill
- performance-optimizationSkill
- react-patternsSkill
- security-auditSkill
- seo-fundamentalsSkill
- state-managementSkill
- structured-dataSkill
- systematic-debuggingSkill
- testing-strategySkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

