The package.json guide
The package.json file is a key element in lots of app codebases based on the Node.js ecosystem.
If you work with JavaScript, or you’ve ever interacted with a JavaScript project, Node.js or a frontend project, you surely met the package.json
file.
What’s that for? What should you know about it, and what are some of the cool things you can do with it?
The package.json
file is kind of a manifest for your project. It can do a lot of things, completely unrelated. It’s a central repository of configuration for tools, for example. It’s also where npm
and yarn
store the names and versions of the package it installed.
The file structure
Here’s an example package.json file:
{
}
It’s empty! There are no fixed requirements of what should be in a package.json
file, for an application. The only requirement is that it respects the JSON format, otherwise it cannot be read by programs that try to access its properties programmatically.
If you’re building a Node.js package that you want to distribute over npm
things change radically, and you must have a set of properties that will help other people use it. We’ll see more about this later on.
This is another package.json:
{
"name": "test-project"
}
It defines a name
property, which tells the name of the app, or package, that’s contained in the same folder where this file lives.
Here’s a much more complex example, which I extracted this from a sample Vue.js application:
{
"name": "test-project",
"version": "1.0.0",
"description": "A Vue.js project",
"main": "src/main.js",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"unit": "jest --config test/unit/jest.conf.js --coverage",
"test": "npm run unit",
"lint": "eslint --ext .js,.vue src test/unit",
"build": "node build/build.js"
},
"dependencies": {
"vue": "^2.5.2"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1",
"babel-eslint": "^8.2.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-jest": "^21.0.2",
"babel-loader": "^7.1.1",
"babel-plugin-dynamic-import-node": "^1.2.0",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"chalk": "^2.0.1",
"copy-webpack-plugin": "^4.0.1",
"css-loader": "^0.28.0",
"eslint": "^4.15.0",
"eslint-config-airbnb-base": "^11.3.0",
"eslint-friendly-formatter": "^3.0.0",
"eslint-import-resolver-webpack": "^0.8.3",
"eslint-loader": "^1.7.1",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-vue": "^4.0.0",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.30.1",
"jest": "^22.0.4",
"jest-serializer-vue": "^0.3.0",
"node-notifier": "^5.1.2",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
"portfinder": "^1.0.13",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-url": "^7.2.1",
"rimraf": "^2.6.0",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"uglifyjs-webpack-plugin": "^1.1.1",
"url-loader": "^0.5.8",
"vue-jest": "^1.0.2",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
there are lots of things going on here:
name
sets the application/package nameversion
indicates the current versiondescription
is a brief description of the app/packagemain
set the entry point for the applicationprivate
if set totrue
prevents the app/package to be accidentally published onnpm
scripts
defines a set of node scripts you can rundependencies
sets a list ofnpm
packages installed as dependenciesdevDependencies
sets a list ofnpm
packages installed as development dependenciesengines
sets which versions of Node this package/app works onbrowserslist
is used to tell which browsers (and their versions) you want to support
All those properties are used by either npm
or other tools that we can use.
Properties breakdown
This section describes the properties you can use in detail. I refer to “package” but the same thing applies to local applications which you do not use as packages.
Most of those properties are only used on the https://www.npmjs.com/, other by scripts that interact with your code, like npm
or others.
name
Sets the package name.
Example:
"name": "test-project"
The name must be less than 214 characters, must not have spaces, it can only contain lowercase letters, hyphens (-
) or underscores (_
).
This is because when a package is published on npm
, it gets its own URL based on this property.
If you published this package publicly on GitHub, a good value for this property is the GitHub repository name.
author
Lists the package author name
Example:
{
"author": "Flavio Copes <your@email.com> (https://flaviocopes.com)"
}
Can also be used with this format:
{
"author": {
"name": "Flavio Copes",
"email": "your@email.com",
"url": "https://flaviocopes.com"
}
}
contributors
As well as the author, the project can have one or more contributors. This property is an array that lists them.
Example:
{
"contributors": [
"Flavio Copes <your@email.com> (https://flaviocopes.com)"
]
}
Can also be used with this format:
{
"contributors": [
{
"name": "Flavio Copes",
"email": "your@email.com",
"url": "https://flaviocopes.com"
}
]
}
bugs
Links to the package issue tracker, most likely a GitHub issues page
Example:
{
"bugs": "https://github.com/flaviocopes/package/issues"
}
homepage
Sets the package homepage
Example:
{
"homepage": "https://flaviocopes.com/package"
}
version
Indicates the current version of the package.
Example:
"version": "1.0.0"
This property follows the semantic versioning (semver) notation for versions, which means the version is always expressed with 3 numbers: x.x.x
.
The first number is the major version, the second the minor version and the third is the patch version.
There is a meaning in these numbers: a release that only fixes bugs is a patch release, a release that introduces backward-compatible changes is a minor release, a major release can have breaking changes.
license
Indicates the license of the package.
Example:
"license": "MIT"
keywords
This property contains an array of keywords that associate with what your package does.
Example:
"keywords": [
"email",
"machine learning",
"ai"
]
This helps people find your package when navigating similar packages, or when browsing the https://www.npmjs.com/ website.
description
This property contains a brief description of the package
Example:
"description": "A package to work with strings"
This is especially useful if you decide to publish your package to npm
so that people can find out what the package is about.
repository
This property specifies where this package repository is located.
Example:
"repository": "github:flaviocopes/testing",
Notice the github
prefix. There are other popular services baked in:
"repository": "gitlab:flaviocopes/testing",
"repository": "bitbucket:flaviocopes/testing",
You can explicitly set the version control system:
"repository": {
"type": "git",
"url": "https://github.com/flaviocopes/testing.git"
}
You can use different version control systems:
"repository": {
"type": "svn",
"url": "..."
}
main
Sets the entry point for the package.
When you import this package in an application, that’s where the application will search for the module exports.
Example:
"main": "src/main.js"
private
if set to true
prevents the app/package to be accidentally published on npm
Example:
"private": true
scripts
Defines a set of node scripts you can run
Example:
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"unit": "jest --config test/unit/jest.conf.js --coverage",
"test": "npm run unit",
"lint": "eslint --ext .js,.vue src test/unit",
"build": "node build/build.js"
}
These scripts are command line applications. You can run them by calling npm run XXXX
or yarn XXXX
, where XXXX
is the command name. Example: npm run dev
.
You can use any name you want for a command, and scripts can do literally anything you want.
dependencies
Sets a list of npm
packages installed as dependencies.
When you install a package using npm or yarn:
npm install <PACKAGENAME>
yarn add <PACKAGENAME>
that package is automatically inserted in this list.
Example:
"dependencies": {
"vue": "^2.5.2"
}
devDependencies
Sets a list of npm
packages installed as development dependencies.
They differ from dependencies
because they are meant to be installed only on a development machine, not needed to run the code in production.
When you install a package using npm or yarn:
npm install --dev <PACKAGENAME>
yarn add --dev <PACKAGENAME>
that package is automatically inserted in this list.
Example:
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1"
}
engines
Sets which versions of Node.js and other commands this package/app work on
Example:
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0",
"yarn": "^0.13.0"
}
browserslist
Is used to tell which browsers (and their versions) you want to support. It’s referenced by Babel, Autoprefixer, and other tools, to only add the polyfills and fallbacks needed to the browsers you target.
Example:
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
This configuration means you want to support the last 2 major versions of all browsers with at least 1% of usage (from the CanIUse.com stats), except IE8 and lower.
(see more)
Command-specific properties
The package.json
file can also host command-specific configuration, for example for Babel, ESLint, and more.
Each has a specific property, like eslintConfig
, babel
and others. Those are command-specific, and you can find how to use those in the respective command/project documentation.
Package versions
You have seen in the description above version numbers like these: ~3.0.0
or ^0.13.0
. What do they mean, and which other version specifiers can you use?
That symbol specifies which updates you package accepts, from that dependency.
Given that using semver (semantic versioning) all versions have 3 digits, the first being the major release, the second the minor release and the third is the patch release, you have these rules:
~
: if you write~0.13.0
, you want to only update patch releases:0.13.1
is ok, but0.14.0
is not.^
: if you write^0.13.0
, you want to update patch and minor releases:0.13.1
,0.14.0
and so on.*
: if you write*
, that means you accept all updates, including major version upgrades.>
: you accept any version higher than the one you specify>=
: you accept any version equal to or higher than the one you specify<=
: you accept any version equal or lower to the one you specify<
: you accept any version lower to the one you specify
There are other rules, too:
- no symbol: you accept only that specific version you specify
latest
: you want to use the latest version available
and you can combine most of the above in ranges, like this: 1.0.0 || >=1.1.0 <1.2.0
, to either use 1.0.0 or one release from 1.1.0 up, but lower than 1.2.0.
→ I wrote 17 books to help you become a better developer, download them all at $0 cost by joining my newsletter
→ JOIN MY CODING BOOTCAMP, an amazing cohort course that will be a huge step up in your coding career - covering React, Next.js - next edition February 2025