WebberUI

Taiwan Business Tax ID Input (React)

A React component that validates Taiwan's 8-digit business tax ID (統一編號) in real time — it implements the 2023 check-digit algorithm (weights 1,2,1,2,1,2,4,1, including the special case where the 7th digit is 7) and shows pass/fail with animated feedback as you type.

This implements the Ministry of Finance's published check-digit algorithm for the Taiwan business tax ID: each digit is multiplied by the weights 1,2,1,2,1,2,4,1, the digit sums of those products are added up, and the total passes if it is divisible by 5 (the rules in force since April 2023, backwards compatible with older tax IDs), with the special case where the 7th digit is 7 handled as well. Validation runs the moment 8 digits are entered — a green check pulses on success, and the whole row shakes with a red outline and an explanation on failure. Pasting a string with noise in it is cleaned automatically down to the digits.

Loading preview…
npx shadcn@latest add https://webberui.com/r/tax-id-input.json

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

<TaxIdInput />

Installation

npx shadcn@latest add https://webberui.com/r/tax-id-input.json

Or, once registries are configured in components.json, install it as @webberui/tax-id-input.

Usage

import { TaxIdInput, validateTaxId } from "@/components/ui/tax-id-input";

<TaxIdInput
  label="統一編號"
  onChange={(value) => console.log(value)}
  onValid={(ok, value) => ok && console.log(`${value} can be invoiced`)}
/>

// The validator can be used on its own (handy for one more check before form submission)
validateTaxId("12345675"); // true (an algorithm test number, not a real company)
validateTaxId("12345678"); // false

Props

PropTypeDefaultDescription
valuestringControlled value (digits only, at most 8); when omitted the component is uncontrolled
defaultValuestring""Initial value in uncontrolled mode
onChange(value: string) => voidCallback when the value changes; the argument is the cleaned digits-only string
onValid(valid: boolean, value: string) => voidCallback when 8 digits have been entered and validation completes; it fires once per value
labelstring"統一編號"Field label text
disabledbooleanfalseDisable the input
namestringForm field name forwarded to the native input
classNamestringForwarded to the outermost container

validateTaxId(input), sanitizeTaxId(raw), and the TaxIdStatus type are also named exports, so the same validation logic can be reused at the form layer.

Accessibility

  • The real input is carried by a single native input (inputMode="numeric", pattern="[0-9]*"); the 8 visual boxes are presentational only and marked aria-hidden, so keyboard behavior and the mobile numeric keypad behave exactly like an ordinary input
  • The validation message sits in an aria-live="polite" region, so state changes are read out by screen readers; aria-invalid is set at the same time when validation fails
  • When the user has "reduce motion" enabled at the system level, the shake, pulse, and caret blink animations are disabled and only the color and text feedback remains

On this page