Telerik blogs

Follow these steps to publish a TypeScript pakcage to npm.

There are not many good articles on publishing a TypeScript package to npm automatically. The best article I have seen is by the one and only Matt Pocock, How to Create an NPM Package. This is wonderful for understanding the package part, but publishing is not well documented. Recently, the npm publish keys have gone away, so we must connect our workflow manually with Trusted Publishing.

Let’s go through all steps to publish a TS npm package automagically!

Create Repository

  1. Create a folder with the name of your package and open it up in VS Code.

  2. Create your repository on GitHub with same name.

  3. Run git init in root.

  4. Create a .gitignore file in root. dist will be where it builds the JavaScript.

    node_modules
    dist
    .env
    .env.*
    

Note: You may not have an .env file, but it is good practice.

  1. Create your README.md with whatever content.
  2. Create a LICENSE file if you want as well. There are packages for this or you can copy and paste.
  3. Run git add . and git commit -m 'first' to commit your first file.
  4. Run git branch -M main to make main the default branch.
  5. Add your remote origin with:
git remote add origin https://github.com/YOUR_REPOSITORY
  1. Push package with git push -u origin main.

Note: My example package will be named format-price.

Create Your Package.json File

  1. Create package.json in root.
{
  "name": "format-price",
  "version": "1.0.0",
  "description": "A demo of a simple TypeScript package that formats a price value.",
  "keywords": [
    "demo",
    "typescript",
    "price",
    "formatting"
  ],
  "homepage": "https://github.com/jdgamble555/format-price",
  "bugs": {
    "url": "https://github.com/jdgamble555/format-price/issues"
  },
  "author": "Jonathan Gamble (https://code.build)",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/jdgamble555/format-price.git"
  },
  "files": [
    "dist"
  ],
  "type": "module",
  "main": "dist/index.js",
  "scripts": {
    "build": "tsc",
    "test": "vitest run",
    "ci": "npm run build && npm test"
  }
}
  • name - name it
  • description - describe it
  • version - use semantic versioning for changesets
  • keywords - add keywords
  • homepage - add your homepage
  • bugs - where to report bugs
  • author - who created this
  • repository - where to view git source
  • files - the folder to install
  • type - use module for TypeScript
  • main - entry point for node
  • scripts
    • build - build TypeScript file
    • test - test with vitest
    • ci - continuous integration command for workflow

Install Dependencies

  • npm i -D typescript to install it as development dependency
  • npm i -D vitest for testing

Config TypeScript

  • Create tsconfig.json file.
{
    "compilerOptions": {
        "esModuleInterop": true,
        "skipLibCheck": true,
        "target": "es2022",
        "allowJs": true,
        "resolveJsonModule": true,
        "moduleDetection": "force",
        "isolatedModules": true,
        "verbatimModuleSyntax": true,
        "strict": true,
        "noUncheckedIndexedAccess": true,
        "noImplicitOverride": true,
        "module": "NodeNext",
        "outDir": "dist",
        "rootDir": "src",
        "sourceMap": true,
        "declaration": true,
        "declarationMap": true
    }
}

See Matt Pocock’s Total TypeScript for more info and if you want prettier, etc.

Add Your TypeScript Content

For this test package, we are updating the price to standard format.

  • /src/index.ts
export const formatPrice = (price: number) => {
    return new Intl.NumberFormat('en-US', {
        style: 'currency',
        currency: 'USD',
    }).format(price);
};

Add Testing Script

  • /src/index.test.ts
import { formatPrice } from "./index.js";
import { test, expect } from "vitest";

test("formatPrice", () => {
    expect(formatPrice(1234.56)).toBe("$1,234.56");
});

test("formatPrice with small value", () => {
    expect(formatPrice(5)).toBe("$5.00");
});

Note: You can test your package with npm run test, which just runs npx vitest run under the hood.

Workflow 1

GitHub has the ability to run scripts once you commit. Every workflow file will be a YAML file with the extension .yml.

Create Default Continuous Integration Workflow

  • .github/workflows/ci.yml
name: CI

on:
    pull_request:
    push:
        branches:
            - main

concurrency:
    group: ${{ github.workflow }}-${{ github.ref }}
    cancel-in-progress: true

jobs:
    ci:
        runs-on: ubuntu-latest

        steps:
            - uses: actions/checkout@v4

            - name: Use Node.js
              uses: actions/setup-node@v4
              with:
                  node-version: '20'

            - name: Install dependencies
              run: npm install

            - name: Run CI
              run: npm run ci
  • This will install node, install dependencies and run the continuous integration script.

GitHub

When you commit your package, you will see the workflow runs in the Action tab on GitHub.

Action tab GitHub

This workflow runs npm run ci, which basically makes sure the tests pass. It is queued first and then run.

npm run cli

And if it does not pass, you will see an error.

error

Your last commit will fail.

commit fail

Which also notifies you in your email.

commit fail email

And, of course, you see a check once you re-commit with tests that pass.

fixed

Versioning with ChangeSet

Use changesets for versioning.

  • Install it with npm i -D @changesets/cli
  • Initialize it with npx changeset init

Create a New Version

  • Run npx changeset
    • Select patch, minor or major for the type of update.
    • Enter a summary for the log describing the change.
    • You will see a new file under .changeset like smooth-ads-lick.md or some random name. This will be the log that will be appended to your root CHANGELOG.md file.
  • Run npx changeset version to automatically update your package version.
    • The generated file smooth-ads-lick.md (will be different name) has been deleted, and your log in the root CHANGELOG.md file has been appended.
# format-price

## 1.0.2

### Patch Changes

- testing for log

## 1.0.1

### Patch Changes

- first patch
  • Commit to GitHub as is:
    • git add .
    • git commit -m 'patch'
    • git push

GitHub

Now, let’s set up for auto publishing.

Go to Settings in your GitHub project, then click Actions and General on the left menu.

Screenshot 2026-03-07 191810.png

  • You need to allow GitHub to publish to npm with read and write permissions.

Optional GitHub Ruleset

Under Rules and then Rulesets, it is wise (you will get a warning anyway) to protect your main branch.

  • Name the ruleset Restrict Deletions or whatever you like.
  • Make sure default is selected under target branches.
  • By default, Restrict deletions and Block force pushes will be selected.

GitHub Ruleset

Workflow 2

Create the second workflow file in the .github/workflows folder called release.yml.

name: Publish Package

on:
    push:
        branches:
            - main

permissions:
    contents: write # required for tags + version PR
    pull-requests: write # required for Version Packages PR
    id-token: write # required for npm trusted publishing (OIDC)

jobs:
    publish:
        runs-on: ubuntu-latest

        steps:
            - uses: actions/checkout@v4
              with:
                  fetch-depth: 0 # Changesets needs tag + commit history

            - uses: actions/setup-node@v4
              with:
                  node-version: '20'
                  registry-url: 'https://registry.npmjs.org'

            - name: Update npm
              run: npm install -g npm@latest

            - name: Install dependencies
              run: npm ci

            - name: Build (if needed)
              run: npm run build --if-present

            - name: Run tests
              run: npm test

            - name: Changesets version / publish
              uses: changesets/action@v1
              with:
                  # When there *are* pending changesets → create/update Version Packages PR
                  version: npm run changeset:version

                  # After the Version Packages PR is merged → publish & create tags
                  publish: npm run changeset:publish
              env:
                  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
                  NPM_CONFIG_PROVENANCE: true

This workflow will run the tests with npm test, then create a PR. Once the PR is merged (this is manual for security—you don’t want this automatic), the version is updated, and that version is published to npm.

Note: Notice the permissions at the top. We need this to allow changes to the version. This is saved in GitHub under Releases and Tags.

Publish to npm

This assumes you have an npm account. You can create one at npm if you don’t have one. Make sure to enable 2FA.

Publish your first version to npm by running.

  • npm login – You will log in with the browser, then enter your 2FA code.
  • Run npm publish --access public to publish a public package.

npm 11 packages

Once you publish, you will see your package published in npm. This means anyone can install it with npm i format-price in their repo.

Note: Our final goal is to publish automatically with a git commit.

Configure GitHub Publishing

Click on the package, then go to Settings.

Settings

  • Create a new Connection to GitHub Actions.

Trusted Publisher page with fields for org or user, repo, workflow filename, environment name - to set up new connection

  • Enter your GitHub username and repository, as well as your YAML file release.yml.

prepped trusted publisher can be edited or deleted

  • You will see a connection enabled for GitHub to be a Trusted Publisher.

Auto Publishing

Now that the connection is setup, you can publish automatically.

  • Create a change to your file.
  • Run npm run build and then whatever other formatting or pre-publishing commands you use.
  • Run npx changeset to create a new version. You can use patch for testing.
  • Create a new commit
    • git add .
    • git commit -m 'auto publish'
    • git push
  • Go to your project on GitHub, and you should see a successful Pull Request.

Pull requests - 1- version packages

successful pull request - no conflicts with base branch

  • Click the Merge pull request button to merge the versioning and changes. It will also auto publish and auto update your versions. Feel free to add any more description info for the PR.
  • Wait a few minutes for the publish to update, and you will now see a Releases option in your GH sidebar.

GitHub sidebar includes Releases

  • Don’t forget to pull the version changes locally with git pull.

You now have a fully automated npm publishing pipeline!

You can install your package anywhere with npm i format-price!

Example Repo: GitHub


Read next: TypeDB: A Graph Database for Things


About the Author

Jonathan Gamble

Jonathan Gamble has been an avid web programmer for more than 20 years. He has been building web applications as a hobby since he was 16 years old, and he received a post-bachelor’s in Computer Science from Oregon State. His real passions are language learning and playing rock piano, but he never gets away from coding. Read more from him at https://code.build/.

 

 

Related Posts

Comments

Comments are disabled in preview mode.