npwp-formatter
v0.1.0
Published
Lightweight, zero-dependency formatter & validator for Indonesian NPWP (tax ID). Supports the 15-digit dotted format and the 16-digit/NIK format, with a moment.js-style chainable API.
Downloads
24
Maintainers
Readme
npwp-formatter
Lightweight, zero-dependency formatter & validator for Indonesian NPWP (Nomor Pokok Wajib Pajak / tax ID).
- 🪶 Tiny & zero-dependency — pure TypeScript, framework-agnostic. Works in Node, the browser, React, Next.js (client & server), React Native, etc.
- 🔢 Handles both the legacy 15-digit format (
XX.XXX.XXX.X-XXX.XXX) and the current 16-digit / NIK format. - ✅ Structural validation plus optional Luhn checksum.
- ⛓️ Two APIs: standalone tree-shakeable functions and chainable wrapper.
- 📦 Ships ESM + CJS +
.d.ts.
Install
npm install npwp-formatterQuick start
import { npwp, formatNpwp, isValidNpwp } from 'npwp-formatter';
// Functional API
formatNpwp('091234567890000'); // '09.123.456.7-890.000'
isValidNpwp('09.123.456.7-890.000'); // true
isValidNpwp('091234567890000', { checksum: true });
// Chainable
npwp('091234567890000').format(); // '09.123.456.7-890.000'
npwp('09.123.456.7-890.000').raw(); // '091234567890000'
npwp('091234567890000').type(); // 'pribadi'
npwp('091234567890000').to16().format(); // '0091234567890000'
`${npwp('091234567890000')}`; // '09.123.456.7-890.000'Format rules
| Length | Output | Notes |
| ------ | ------------------------------ | ------------------------------------------------ |
| 15 | XX.XXX.XXX.X-XXX.XXX | Legacy dotted display format. |
| 16 | plain digits (no punctuation) | Official 16-digit / NIK format carries no marks. |
| other | cleaned digits, unchanged | Input is normalised but not grouped. |
15 vs 16 digit: Since 1 July 2024 NPWP uses 16 digits. For resident individuals it equals the 16-digit NIK; for entities / non-resident individuals it is the old 15-digit NPWP with a leading
0. The dotted format only applies to the 15-digit form.
API
Functions
| Function | Description |
| --- | --- |
| cleanNpwp(value) | Strip everything but digits. Nullish-safe (''). |
| formatNpwp(value) | Display form (see table above). |
| isValidNpwp(value, { checksum? }) | Structural validity; checksum: true adds Luhn. |
| luhnIsValid(digits) | Raw Luhn (mod-10) check over a digit string. |
| getNpwpType(value) | 'pribadi' \| 'badan' \| 'unknown'. |
| to16(value) | 15-digit → 16-digit (prepend 0). |
| to15(value) | 16-digit corporate form → 15-digit (strip leading 0). |
Constants: NPWP_15_LENGTH, NPWP_16_LENGTH.
npwp(value) → Npwp
| Member | Returns | Description |
| --- | --- | --- |
| .raw() / .digits | string | Normalised digits. |
| .length | number | Digit count. |
| .format() / .toString() | string | Display form. |
| .isValid(options?) | boolean | Structural (+ optional Luhn). |
| .type() | NpwpType | Taxpayer type. |
| .to16() / .to15() | Npwp | New instance (immutable). |
Types
type NpwpType = 'pribadi' | 'badan' | 'unknown';
interface IsValidOptions { checksum?: boolean }