Skip to main content

pkg-types vs read-pkg vs read-package-up: Reading package.json in Node.js (2026)

·PkgPulse Team

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 bin string 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

Featurepkg-typesread-pkgread-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
Dependencies0Fewread-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 →

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.