How to create a JavaScript package with npm

Reusable browser and Node.js helpers are easier to test when they are shaped as npm packages instead of copied between projects. A small package needs metadata, one public entry point, and a consumer check that proves another project can import it.

npm init creates the initial package.json, while npm pkg set records the fields that control package identity and module loading. An ES module entry point with exports lets consumers import only the public file.

Local testing should happen before publishing to the registry. Installing the packed .tgz file into a separate consumer project catches missing files, the wrong module type, and broken exports before any release step is considered.

Steps to create a JavaScript package with npm:

  1. Create the package directory.
    $ mkdir format-title
  2. Enter the package directory.
    $ cd format-title
  3. Create the initial package metadata.
    $ npm init --yes
    Wrote to package.json:
    ##### snipped #####
  4. Set the package identity and public module entry point.
    $ npm pkg set name=format-title version=1.0.0 description="Format display titles" type=module exports=./index.js

    type=module tells Node.js to treat .js files as ES modules. The exports field exposes index.js as the package's public entry point.

  5. Limit the files included in the package tarball.
    $ npm pkg set 'files=["index.js"]' --json

    The files field keeps local tests, notes, and build artifacts out of the package unless they are deliberately included.

  6. Check the saved package metadata.
    $ npm pkg get name version type exports --json
    {
      "name": "format-title",
      "version": "1.0.0",
      "type": "module",
      "exports": "./index.js"
    }
  7. Create the exported package entry point.
    index.js
    export function formatTitle(value) {
      return value
        .trim()
        .toLowerCase()
        .replace(/\b[a-z]/g, (letter) => letter.toUpperCase());
    }
  8. Pack the package into a local tarball.
    $ npm pack
    npm notice Tarball Contents
    npm notice 145B index.js
    npm notice 327B package.json
    npm notice filename: format-title-1.0.0.tgz
    ##### snipped #####
    format-title-1.0.0.tgz
  9. Return to the parent directory.
    $ cd ..
  10. Create a separate consumer project.
    $ mkdir format-title-consumer
  11. Enter the consumer project.
    $ cd format-title-consumer
  12. Create the consumer project metadata.
    $ npm init --yes
    Wrote to package.json:
    ##### snipped #####
  13. Install the packed package into the consumer project.
    $ npm install ../format-title/format-title-1.0.0.tgz
    
    added 1 package in 205ms
  14. Create the consumer smoke test.
    demo.mjs
    import { formatTitle } from "format-title";
     
    console.log(`Imported formatTitle(): ${formatTitle(" release NOTES ")}`);
  15. Run the consumer smoke test.
    $ node demo.mjs
    Imported formatTitle(): Release Notes
  16. Return to the parent directory.
    $ cd ..
  17. Remove the temporary consumer project and packed tarball.
    $ rm -r format-title-consumer format-title/format-title-1.0.0.tgz

    Keep the consumer directory if it should become a permanent integration fixture.