How to add a new package to a monorepo

Greg Foster
Greg Foster
Graphite software engineer
Try Graphite

Table of contents

A package is a discrete, version‑controlled self‑contained component—like a library, service, or shared module—that lives inside the monorepo but can be built, tested, and published on its own.

Monorepo packages enable:

  • Clear modularity.
  • Shared code reuse.
  • Streamlined dependency updates with atomic commits across packages.
  • Efficient tooling like npm or yarn workspaces, Lerna, Nx, Turborepo, etc.

They're useful because they let teams logically separate concerns within one repo while supporting fine-grained CI, dependency hoisting, and version control.

Using packages provides modularity, reusability, and clear separation of responsibilities. They also support atomic changes: you can update shared logic and consumers in a single commit. Monorepos enhance this by letting packages live side by side, simplifying dependency management and code sharing.

  1. A working git monorepo with tooling like pnpm/workspaces, yarn workspaces, or Lerna.
  2. Each package ideally has its own folder (e.g., /packages/foo/), with a package.json, source code, and build/test definition.
  3. A CI workflow that supports selective builds and tests based on changed packages.
  4. Optionally: Graphite installed and configured for stacked pull requests and code review optimization.
Terminal
cd path/to/monorepo
mkdir -p packages/<new-package>
cd packages/<new-package>
Terminal
npm init -y
# edit package.json:
# {
# "name": "@your-scope/new-package",
# "version": "0.1.0",
# "main": "dist/index.js",
# "scripts": {
# "build": "tsc",
# "test": "jest",
# "lint": "eslint src"
# }
# }

Add dependencies (e.g., npm install lodash --save or npm install typescript --save-dev).

Create standard folders:

Terminal
src/index.ts # entry point
__tests__/index.test.ts

Implement basic code and a test (for example, using Jest).

Add to repo-level configuration (package.json or config files):

Terminal
{
"workspaces": ["packages/*"]
}

Or add in Lerna/Turborepo/other: recognize packages/<new-package>.

Terminal
npm install
npm run build -w @scope/new-package
npm run test -w @scope/new-package

Verify that the package builds and passes all tests.

  • Add the new package as a dependency to another package:
Terminal
npm install @your-scope/new-package --workspace=packages/consumer
  • Use import in code:
Terminal
import { foo } from "@your-scope/new-package";

Run consumer's build and tests to confirm integration.

Ensure CI runs necessary steps for the new package; for selective CI, include it in changeset logic.

Add a README.md in packages/<new-package> with purpose, install instructions, and usage examples.

Commit changes:

Terminal
git add packages/<new-package>
git commit -m "feat: add new package `<new-package>`"
git push origin <branch>

Open a PR. This is where Graphite shines.

  1. Stacked pull requests Graphite allows breaking a large change (e.g. adding a package and then usage tweaks) into sequential stacked PRs, maintaining developer velocity without waiting on merges.

  2. Merge queue & conflict prevention Graphite can rebase stacked PRs automatically and detect conflicts ahead of time, keeping the main branch green and reducing manual rebasing.

  3. Code‑aware AI-assisted reviews (Diamond) Graphite's AI reviewer analyzes PR diffs, suggests improvements, summarizes changes, and can even propose fixes — saving reviewers' time.

  4. CI efficiency for monorepos Graphite optimizes CI runs in monorepo setups — running builds/tests only where needed based on code changes, reducing wasteful runs.

  5. Team coordination & visibility Unified PR inboxes, Slack notifications, and assignment tools in Graphite help distributed teams stay aligned on which packages and PRs need review.

  • Adhere to consistent lint/build/test patterns across packages.
  • Name packages clearly (@org/foo).
  • Keep scopes narrow: each package should serve a single responsibility.
  • Version and update strategy: all-or-none version bumps (monolithic versioning) or independent SEMVER per package.
  • Update docs (e.g., root-level index or workspace README).
StepAction
Create package folder
Init package.json and scripts
Write source and tests
Update workspace config
Run build and tests
Integrate into other packages
Adjust CI
Write documentation
Commit and open PR
Use Graphite for smooth review and merging

Adding a new package in a monorepo is a repeatable process that promotes modularity, streamlines dependency management, and encourages atomic cross-package changes. Integrating Graphite further amplifies developer efficiency by automating PR workflows, reducing merge conflicts, and scaling code review processes across teams.

Built for the world's fastest engineering teams, now available for everyone