The Basics of Package.json explained

The Basics of Package.json explained

Core element of Node.js ecosystem - package.json

ByMario Kandut

honey pot logo

Europe’s developer-focused job platform

Let companies apply to you

Developer-focused, salary and tech stack upfront.

Just one profile, no job applications!

The package.json file is a core element in the Node.js ecosystem and is basic for understanding and working with Node.js, NPM, modern JavaScript and JavaScript frameworks and libraries. The package.json is used as a manifest about applications, modules and packages. It's a tool to make modular and efficient applications.

This article is based on Node v16.14.0.

💰 The Pragmatic Programmer: journey to mastery. 💰 One of the best books in software development, sold over 200,000 times.

Understanding the basics of package.json is essential, so I outline in the following article the most common and important properties of the package.json file.

Let's start with initiating a project to create a basic package.json.

Create Folder mkdir super-mario and run npm init (option with the -y flag to answer yes to every question).

This command will create this package.json:

  "name": "super-mario",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  "author": "",
  "license": "ISC"

Properties in package.json

Let's go over the properties from the generated package.json.


This is the name for the project and is optional if the project is private. When a npm package is published, the name will be used as an URl. Hence, when the package gets published the name is required and must be unique on the npm repository. It has requirements to be URL-safe. It should be:

  • less than 214 characters
  • should not include spaces
  • can only contain lowercase letters, hyphens (-) or underscores (_)
  • The name can be prefixed with a scope (i.e.: @angular/angular-cli).

A good value if you have publicly published this package on Github is the repository name.


A version number that should be understandable by node-semver. The version property is optional for private and required for public modules.


A brief description for the project, optional for both, private and public, but useful if you publish your package to npm to provide more information.


The entry file/entry point for the package. When you import this package in an application, that’s where the application will search for the module exports.


The scripts key expects an object with script names as keys and commands as values, so it basically defines a set of node scripts you can execute.

These scripts are command line applications. You can run them by calling npm run COMMAND or yarn COMMAND.

When we have a look at the package.json above, we see the test key in the scripts.

    "test": "echo \"Error: no test specified\" && exit 1"

So we can run npm run test in the commandline and would get Error: no test specified logged.

There are no limitations in the command name and scripts can do things like starting your project on a local server, building for production or running your tests.

Typically, in the scripts commands you'll make the most manual changes in your package.json file.


An array of keywords to helps with finding the module on the npm repository.


This property lists the package author name and expects an object with keys for the name, email and url.

Possible formats are:

"author": "Mario Kandut <[email protected]> ("
"author": {
    "name": "Mario Kandut",
    "email": "[email protected]",
    "url": ""


Indicates the license of the package and expects a license name using its SPDX identifier. It defaults to the ISC license, and MIT would be another popular license choice. You can also use UNLICENSED for projects that are private and closed-source.

There are many more properties in the package.json than generated by npm init.


This property defines where the source code of the package repository is located. Typically, this would be a public GitHub/Gitlab repo, with the repository array noting that the type of version control is git, and the URL of the repo itself.

The repository property would look like this:

  "repository": {
      "type": "git",
      "url": ""

You can also use the prefix for github or gitlab.

"repository": "github:mariokandut/REPOSITORY-NAME"
"repository": "gitlab:mariokandut/REPOSITORY-NAME"

Managing Dependencies

One of npm’s main strength is the ability to easily manage project’s dependencies. Hence, the package.json centers mostly around specifying the dependencies for a project. There are the regular dependencies, but there can also be devDependencies, peerDependencies, optionalDependencies and bundledDependencies.


The dependencies property is where dependencies, the other modules that this module uses, are defined. It takes an object that has the name and version at which each dependency should be used. You'll frequently find carets (^) and tildes (~) included with package versions, these are the notation for version range.

The dependencies property could look something like this:

  "dependencies": {
    "async": "^0.2.10",
    "npm2es": "~0.4.2",
    "optimist": "~0.6.0",
    "request": "~2.30.0",
    "skateboard": "^1.5.1",
    "split": "^0.3.0",
    "weld": "^0.2.2"

When you install a package using npm or yarn, npm install <PACKAGENAME> or yarn add <PACKAGENAME>, that package is automatically inserted in this list.

"dependencies": {
    "react": "^16.10.2",
    "react-dom": "^16.10.2",
    "react-helmet": "^5.2.1",


The devDependencies property is almost identical to the dependencies property, when it comes to structure. There is one key difference. The dependencies property is used to define the dependencies that a module needs to run in production. The devDependencies property is used to define the dependencies the module needs to run in development.


"devDependencies": {
    "@types/react-helmet": "^6.1.0",
    "@types/react-typist": "^2.0.1",
    "@typescript-eslint/eslint-plugin": "^4.1.0",

When you install a package using npm or yarn, npm install --dev <PACKAGENAME> or yarn add --dev <PACKAGENAME>, that package is automatically inserted in this list.


In some cases, you want to express the compatibility of your package with a host tool or library, while not necessarily doing a require of this host. This is usually referred to as a plugin. Notably, your module may be exposing a specific interface, expected and specified by the host documentation.

For example:

  "name": "tea-latte",
  "version": "1.3.5",
  "peerDependencies": {
    "tea": "2.x"

This ensures your package tea-latte can be installed along with the second major version of the host package tea only.


If a dependency can be used, but you would like npm to proceed if it cannot be found or fails to install, then you may put it in the optionalDependencies object. This is a map of package name to version or url, just like the dependencies object. The difference is that build failures do not cause installation to fail. Running npm install --no-optional will prevent these dependencies from being installed.

It is still your program's responsibility to handle the lack of the dependency. For example, something like this:

try {
  var foo = require('foo');
  var fooVersion = require('foo/package.json').version;
} catch (er) {
  foo = null;
if (notGoodFooVersion(fooVersion)) {
  foo = null;
// .. then later in your program ..
if (foo) {

Entries in optionalDependencies will override entries of the same name in dependencies, so it's usually best to only put in one place.


This defines an array of package names that will be bundled when publishing the package.

In cases where you need to preserve npm packages locally or have them available through a single file download, you can bundle the packages in a tarball file by specifying the package names in the bundledDependencies array and executing npm pack.

For example:

  "name": "awesome-web-framework",
  "version": "1.0.0",
  "bundledDependencies": ["renderized", "super-streams"]

More Useful Configuration Keys

There are more configurations that can optionally go into your project’s package.json file:


You can specify the version of node that your stuff works on:

  "engines": {
    "node": ">=0.10.3 <15"

And, like with dependencies, if you don't specify the version (or if you specify "*" as the version), then any version of node will do.

You can also use the "engines" field to specify which versions of npm are capable of properly installing your program. For example:

  "engines": {
    "npm": "~1.0.20"

Unless the user has set the engine-strict config flag, this field is advisory only and will only produce warnings when your package is installed as a dependency.


A config object can be used to set configuration parameters used in package scripts that persist across upgrades. For instance, if a package had the following:

  "name": "foo",
  "config": {
    "port": "8080"

Then with a start command that referenced the npm_package_config_port environment variable. A user could override that by doing npm config set foo:port 8001.


If you set "private": true in your package.json, then npm will refuse to publish it. This is a way to prevent accidental publication of private repositories.


If your module is meant to be used client-side the browser field should be used instead of the main field. This is helpful to hint users that it might rely on primitives that aren't available in Node.js modules. (e.g. window)


The URL for the home page of the project.


A URL where issues and bugs can be reported. This will often be an URL to the Github issues page for a project.


  • The package.json file acts like a manifest for your application.
  • The strength is in managing dependencies (prod, dev, peer, optional or bundled).
  • Read the article carefully, so you don't publish your package publicly.
  • There are many configuration options, have a look at the official documentation for all the possible options.

Thanks for reading and if you have any questions, use the comment function or send me a message @mariokandut.

If you want to know more about Node, have a look at these Node Tutorials.

References (and Big thanks):

NPM, Node, NodeJS dev, NodeSource

More node articles:

Getting started with Webpack

How to list/debug npm packages?

How to specify a Node.js version

How to create a web server in Node.js

How to dynamically load ESM in CJS

How to convert a CJS module to an ESM

How to create a CJS module

How to stream to an HTTP response

How to handle binary data in Node.js?

How to use streams to ETL data?

How to connect streams with pipeline?

How to handle stream errors?

How to connect streams with pipe?

What Is a Node.js Stream?

Handling Errors in Node (asynchronous)

Handling Errors in Node.js (synchronous)

Introduction to errors in Node.js

Callback to promise-based functions

ETL: Load Data to Destination with Node.js

ETL: Transform Data with Node.js

ETL: Extract Data with Node.js

Event Emitters in Node.js

How to set up SSL locally with Node.js?

How to use async/await in Node.js

What is an API proxy?

How to make an API request in Node.js?

How does the Event Loop work in Node.js

How to wait for multiple Promises?

How to organize Node.js code

Understanding Promises in Node.js

How does the Node.js module system work?

Set up and test a .env file in Node

How to Use Environment Variables in Node

How to clean up node modules?

Restart a Node.js app automatically

How to update a Node dependency - NPM?

What are NPM scripts?

How to uninstall npm packages?

How to install npm packages?

How to create a package.json file?

What Is the Node.js ETL Pipeline?

What is data brokering in Node.js?

How to read and write JSON Files with Node.js?

What is package-lock.json?

How to install Node.js locally with nvm?

How to update Node.js?

How to check unused npm packages?

What is the Node.js fs module?

What is Semantic versioning?

The Basics of Package.json explained

How to patch an NPM dependency

What is NPM audit?

Beginner`s guide to NPM

Getting started with Node.js

Scroll to top ↑