pkg-types vs read-pkg vs read-package-up: Reading package.json in Node.js (2026)
TL;DR
pkg-types is the UnJS package for reading package.json — TypeScript types for package.json fields, finds the nearest package.json, resolves workspaces, and exports typed utilities. read-pkg reads and normalizes a single package.json — parses, validates, normalizes fields (like man, bin). read-package-up (formerly read-pkg-up) walks up directories to find the nearest package.json — useful for tools that need to find the project root. In 2026: pkg-types for typed package.json access, read-package-up for traversal, read-pkg for normalized parsing.
Key Takeaways
- pkg-types: ~10M weekly downloads — UnJS, TypeScript types for package.json, workspace resolution
- read-pkg: ~15M weekly downloads — reads + normalizes package.json, Sindre Sorhus
- read-package-up: ~15M weekly downloads — finds nearest package.json by walking up directories
- pkg-types provides full TypeScript types for every package.json field
- read-pkg normalizes package.json (e.g., converts
binstring to object format) - All three are commonly used by CLI tools, bundlers, and linters
pkg-types
pkg-types — typed package.json utilities:
Read package.json
import { readPackageJSON, findWorkspaceDir, resolvePackageJSON } from "pkg-types"
// Read the nearest package.json:
const pkg = await readPackageJSON()
// → { name: "my-app", version: "1.0.0", dependencies: {...}, ... }
// Read from a specific path:
const pkg2 = await readPackageJSON("/path/to/project")
// Find the nearest package.json path:
const pkgPath = await resolvePackageJSON()
// → "/Users/royce/project/package.json"
// Find workspace root:
const workspaceRoot = await findWorkspaceDir()
// → "/Users/royce/monorepo" (root with workspaces config)
TypeScript types
import type { PackageJson } from "pkg-types"
// Full TypeScript types for package.json fields:
const pkg: PackageJson = {
name: "pkgpulse",
version: "1.0.0",
type: "module",
main: "./dist/index.cjs",
module: "./dist/index.js",
types: "./dist/index.d.ts",
exports: {
".": {
import: { types: "./dist/index.d.ts", default: "./dist/index.js" },
require: { types: "./dist/index.d.cts", default: "./dist/index.cjs" },
},
},
dependencies: { "react": "^19.0.0" },
devDependencies: { "typescript": "^5.5.0" },
peerDependencies: { "react": ">=18" },
scripts: { build: "tsup", test: "vitest" },
}
// Type-safe access:
pkg.name // string | undefined
pkg.version // string | undefined
pkg.exports // Exports | undefined (full conditional exports type)
pkg.type // "module" | "commonjs" | undefined
TSConfig utilities
import { readTSConfig, resolveTSConfig } from "pkg-types"
// Read tsconfig.json:
const tsconfig = await readTSConfig()
// → { compilerOptions: { target: "ES2022", ... }, include: [...] }
// Find the nearest tsconfig.json:
const tsconfigPath = await resolveTSConfig()
// → "/Users/royce/project/tsconfig.json"
read-pkg
read-pkg — read and normalize package.json:
Basic usage
import { readPackage } from "read-pkg"
// Read from current directory:
const pkg = await readPackage()
// → { name: "my-app", version: "1.0.0", ... }
// Read from specific directory:
const pkg2 = await readPackage({ cwd: "/path/to/project" })
// Normalize (converts shorthand fields to full format):
const pkg3 = await readPackage({ normalize: true })
Normalization
import { readPackage } from "read-pkg"
// Before normalization:
// package.json: { "bin": "./cli.js", "man": "./man/doc.1" }
const pkg = await readPackage({ normalize: true })
// After normalization:
// pkg.bin → { "my-app": "./cli.js" } (string → object)
// pkg.man → ["./man/doc.1"] (string → array)
// pkg.repository → { type: "git", url: "..." } (string → object)
Sync version
import { readPackageSync } from "read-pkg"
// Synchronous read:
const pkg = readPackageSync()
const name = pkg.name
const version = pkg.version
read-package-up
read-package-up — find nearest package.json:
Basic usage
import { readPackageUp } from "read-package-up"
// Walks up from cwd to find nearest package.json:
const result = await readPackageUp()
if (result) {
console.log(result.packageJson.name) // Package name
console.log(result.path) // Full path to package.json
// → "/Users/royce/project/package.json"
}
// From a specific directory:
const result2 = await readPackageUp({
cwd: "/Users/royce/project/src/utils/deep/nested",
})
// Walks up: nested → deep → utils → src → project (found!)
Use case: Finding project root
import { readPackageUp } from "read-package-up"
import path from "node:path"
async function findProjectRoot(startDir?: string) {
const result = await readPackageUp({ cwd: startDir })
if (!result) throw new Error("No package.json found")
return path.dirname(result.path)
}
// Usage in a CLI tool:
const projectRoot = await findProjectRoot()
console.log(`Project root: ${projectRoot}`)
Use case: Getting package info from anywhere
import { readPackageUp } from "read-package-up"
// In a deeply nested file, find the package it belongs to:
async function getPackageInfo() {
const result = await readPackageUp()
if (!result) return null
return {
name: result.packageJson.name,
version: result.packageJson.version,
root: path.dirname(result.path),
}
}
// Useful for:
// - CLI tools that need the project name
// - Bundlers that need package.json metadata
// - Linters that need to know project boundaries
Common Patterns
Version detection
import { readPackageJSON } from "pkg-types"
// Check if a dependency is installed and get its version:
async function getDependencyVersion(name: string): Promise<string | null> {
try {
const pkg = await readPackageJSON(`node_modules/${name}`)
return pkg.version ?? null
} catch {
return null
}
}
const reactVersion = await getDependencyVersion("react")
// → "19.0.0" or null
Workspace detection
import { readPackageJSON, findWorkspaceDir } from "pkg-types"
async function getWorkspacePackages() {
const rootDir = await findWorkspaceDir()
if (!rootDir) return []
const rootPkg = await readPackageJSON(rootDir)
const workspaces = rootPkg.workspaces
if (!workspaces) return []
// workspaces could be string[] or { packages: string[] }
const patterns = Array.isArray(workspaces)
? workspaces
: workspaces.packages ?? []
return patterns
}
Feature Comparison
| Feature | pkg-types | read-pkg | read-package-up |
|---|---|---|---|
| Read package.json | ✅ | ✅ | ✅ (with traversal) |
| Walk up directories | ✅ (resolvePackageJSON) | ❌ | ✅ |
| TypeScript types | ✅ (full) | ✅ (basic) | ✅ (via read-pkg) |
| Normalization | ❌ | ✅ | ✅ |
| TSConfig reading | ✅ | ❌ | ❌ |
| Workspace detection | ✅ | ❌ | ❌ |
| Sync API | ❌ | ✅ | ✅ |
| Dependencies | 0 | Few | read-pkg |
| UnJS ecosystem | ✅ | ❌ | ❌ |
| Weekly downloads | ~10M | ~15M | ~15M |
When to Use Each
Use pkg-types if:
- Need full TypeScript types for package.json fields
- Want workspace and tsconfig.json utilities
- In the UnJS ecosystem
- Building tools that need typed package metadata
Use read-pkg if:
- Need normalized package.json (shorthand fields expanded)
- Want a synchronous API option
- Building tools that parse and validate package.json
Use read-package-up if:
- Need to find the nearest package.json from any directory
- Building a CLI tool that runs from anywhere in the project
- Need both the parsed content and the file path
Methodology
Download data from npm registry (weekly average, February 2026). Feature comparison based on pkg-types v1.x, read-pkg v9.x, and read-package-up v11.x.
Compare package utilities and developer tooling on PkgPulse →