What Is It?
@tushar_rayamajhi/roman_converter is a zero-dependency JavaScript/TypeScript library that does far more than the typical "convert a number to Roman numerals" package. It ships with eight distinct feature categories, three of which you will not find anywhere else on npm.
GitHub: tusharrayamajhi/roman-to-decimal
Live demo: roman_converter package page
Installation
npm install @tushar_rayamajhi/roman_converter
# or
yarn add @tushar_rayamajhi/roman_converter
# or
pnpm add @tushar_rayamajhi/roman_converter
Requires Node.js ≥ 18. Zero external dependencies. Full TypeScript definitions included — no @types/ package needed.
1. Core Conversion (1–3,999)
The foundation of the library:
import { toRoman, fromRoman, isValidRoman, isValidNumber } from '@tushar_rayamajhi/roman_converter'
toRoman(2024) // "MMXXIV"
toRoman(42) // "XLII"
toRoman(42, { lowercase: true }) // "xlii"
fromRoman('MCMXCIV') // 1994
fromRoman('xlii') // 42 (case-insensitive input)
isValidRoman('XLII') // true
isValidRoman('IIII') // false (invalid form — strict validation)
isValidNumber(3999) // true
isValidNumber(4000) // false
A key difference from naive implementations: fromRoman validates strict Roman numeral form, so IIII (four I's) correctly returns false — only IV is accepted.
2. Arithmetic Operations
All four arithmetic operations accept Roman numeral strings or integers interchangeably. You can freely mix them:
import { add, subtract, multiply, divide } from '@tushar_rayamajhi/roman_converter'
add('XIV', 'III') // "XVII" (14 + 3)
add(10, 'V') // "XV" (mixed input)
subtract('X', 'IV') // "VI" (10 - 4)
multiply('V', 'III') // "XV" (5 × 3)
divide('X', 'III') // "III" (10 ÷ 3, floor division)
Results are always returned as Roman numeral strings. A RangeError is thrown if the result falls outside 1–3,999.
All functions also accept an options object: { lowercase: true } to return lowercase output.
3. Utilities
import { range, sort, compare, breakdown, batchToRoman, batchFromRoman } from '@tushar_rayamajhi/roman_converter'
// Generate a range of Roman numerals
range(1, 5) // ["I", "II", "III", "IV", "V"]
range(10, 1, -3) // ["X", "VII", "IV", "I"] (negative step)
// Sort mixed arrays (preserves input type)
sort(['X', 'V', 'I', 'M']) // ["I", "V", "X", "M"]
sort([10, 'V', 1, 'M'], 'desc') // ["M", 10, "V", 1]
// Compare two values
compare('X', 'V') // 1 (X > V)
compare('V', 'V') // 0 (equal)
compare('I', 'V') // -1 (I < V)
// Decompose a Roman numeral into its additive parts
breakdown('MCMXCIX')
// [
// { numeral: "M", value: 1000 },
// { numeral: "CM", value: 900 },
// { numeral: "XC", value: 90 },
// { numeral: "IX", value: 9 }
// ]
// Batch conversion
batchToRoman([1, 5, 10, 50, 100, 500, 1000])
// ["I", "V", "X", "L", "C", "D", "M"]
batchFromRoman(["I", "V", "X"])
// [1, 5, 10]
sort preserves the input type: if you pass strings, you get strings back; integers stay integers. Both Roman strings and integers can be in the same array.
4. Extended Numerals — Unique Feature #1
Range: 1–3,999,999
Standard Roman numerals top out at 3,999 (MMMCMXCIX). This library extends the system using parenthetical vinculum notation — a line over a numeral historically multiplied it by 1,000. The parenthetical form is the ASCII-safe equivalent:
| Symbol | Value |
|---|---|
(V) | 5,000 |
(X) | 10,000 |
(L) | 50,000 |
(C) | 100,000 |
(D) | 500,000 |
(M) | 1,000,000 |
import { toExtendedRoman, fromExtendedRoman, isValidExtendedNumber } from '@tushar_rayamajhi/roman_converter'
toExtendedRoman(4000) // "(IV)"
toExtendedRoman(1000000) // "(M)"
toExtendedRoman(1999999) // "(M)(CM)(XC)(IX)CMXCIX"
fromExtendedRoman('(M)') // 1000000
fromExtendedRoman('(IV)') // 4000
isValidExtendedNumber(3999999) // true
isValidExtendedNumber(4000000) // false
No other npm package handles extended Roman numerals beyond 3,999.
5. Roman Clock / Time
Full time conversion with multiple formatting options:
import { toRomanTime, fromRomanTime, nowInRoman } from '@tushar_rayamajhi/roman_converter'
// 24-hour format (default)
toRomanTime('14:30') // "XIV:XXX"
// 12-hour format
toRomanTime('14:30', { format: '12h' }) // "II:XXX"
// Include seconds
toRomanTime('14:30:45', { seconds: true }) // "XIV:XXX:XLV"
// Meridiem (AM/PM)
toRomanTime('14:30', { meridiem: true }) // "XIV:XXX PM"
// Midnight — zero minutes shown as · (middle dot, classical "nulla")
toRomanTime('00:00') // "XII:·"
// Parse a Roman time string back
fromRomanTime('XIV:XXX')
// { hours: 14, minutes: 30, seconds: 0, formatted: '14:30' }
// Current system time
nowInRoman() // e.g. "XIV:XXX"
nowInRoman({ format: '12h', seconds: true }) // e.g. "II:XXX:XLV"
The zero-minutes representation using the middle dot (·) is historically accurate — classical Latin used "nulla" for zero.
6. Classical Latin Words — Unique Feature #2
Range: 1–3,999
import { toWords, fromWords } from '@tushar_rayamajhi/roman_converter'
toWords(1) // "unus"
toWords(4) // "quattuor"
toWords(18) // "duodeviginti" ("two from twenty" — subtraction form)
toWords(19) // "undeviginti" ("one from twenty")
toWords(42) // "quadraginta duo"
toWords(100) // "centum"
toWords(1000) // "mille"
toWords(2000) // "duo milia" (neuter nominative plural)
toWords(3000) // "tria milia"
toWords(3999) // "tria milia nongenti nonaginta novem"
// Reverse: Latin words → integer
fromWords('quadraginta duo') // 42
fromWords('duo milia') // 2000
The library handles the nuances of classical Latin correctly:
- Subtraction forms for x8 and x9 (e.g., 18 = duodeviginti, not decem octo)
- Correct thousand plurals — "mille" (1000), "duo milia" (2000), "tria milia" (3000)
- Neuter nominative for numbers preceding milia
7. Roman Fractions (Uncia) — Unique Feature #3
Ancient Roman commerce used a base-12 fraction system with the as as the unit. Each twelfth was called an uncia — the origin of our words "ounce" and "inch":
| Symbol | Name | Twelfths | Decimal |
|---|---|---|---|
| · | uncia | 1/12 | 0.0833 |
| ·· | sextans | 2/12 | 0.1667 |
| ··· | quadrans | 3/12 | 0.25 |
| ···· | triens | 4/12 | 0.3333 |
| ····· | quincunx | 5/12 | 0.4167 |
| S | semis | 6/12 | 0.5 |
| S· | septunx | 7/12 | 0.5833 |
| S·· | bes | 8/12 | 0.6667 |
| S··· | dodrans | 9/12 | 0.75 |
| S···· | dextans | 10/12 | 0.8333 |
| S····· | deunx | 11/12 | 0.9167 |
| AS | as | 12/12 | 1.0 |
import { toUncia, fromUncia, unciaInfo, isValidUncia, listUncia } from '@tushar_rayamajhi/roman_converter'
// Convert decimal to uncia symbol
toUncia(0.5) // "S" (semis — half)
toUncia(0.25) // "···" (quadrans — quarter)
toUncia(2/3) // "S··" (bes — eight twelfths)
toUncia(1/12) // "·" (uncia — one twelfth)
toUncia(1) // "AS" (as — whole unit)
// Convert symbol to decimal
fromUncia('S') // 0.5
fromUncia('···') // 0.25
// Get full information for a symbol
unciaInfo('S')
// { twelfths: 6, symbol: 'S', name: 'semis', decimal: 0.5 }
// List all 12 uncia entries
listUncia()
// [{ twelfths: 12, symbol: 'AS', name: 'as', decimal: 1 }, ...]
The toUncia function rounds the input to the nearest twelfth — so 0.3 becomes ··· (quadrans = 0.25, the closest).
8. React Hooks
import { useRoman, useRomanClock } from '@tushar_rayamajhi/roman_converter/react'
// Synchronized integer ↔ Roman state
function RomanCounter() {
const { roman, integer, increment, decrement, set, reset } = useRoman(1)
return (
<div>
<p>{roman} ({integer})</p> {/* "I" (1) → "II" (2) → … */}
<button onClick={() => increment()}>+1</button>
<button onClick={() => increment(5)}>+5</button>
<button onClick={() => decrement()}>−1</button>
<button onClick={() => set('X')}>Set to X</button>
<button onClick={reset}>Reset</button>
</div>
)
}
// Live Roman numeral clock — updates every second
function RomanClock() {
const { time, hours, minutes, seconds } = useRomanClock({
format: '24h',
seconds: true,
})
return <span className="font-mono">{time}</span>
// e.g. "XIV:XXX:XLV"
}
useRoman exposes:
roman— current Roman numeral stringinteger— current integerisValid— whether integer is in range 1–3,999set(value)— accepts integer or Roman stringincrement(step?)/decrement(step?)— clamped to 1–3,999reset()— returns to initial value
useRomanClock accepts the same options as toRomanTime() and returns { time, hours, minutes, seconds }.
9. CLI
Install globally or use npx:
# Convert
roman 2024 # MMXXIV
roman XLII # 42
# Arithmetic
roman add XIV III # XVII
roman sub X IV # VI
roman mul V III # XV
roman div X III # III
# Utilities
roman range 1 5 # I, II, III, IV, V
roman sort X V I M # I, V, X, M
roman breakdown MCMXCIX # M=1000, CM=900, XC=90, IX=9
# Extended numerals
roman ext 1000000 # (M)
roman ext "(M)" # 1000000
# Time
roman time # current time
roman time 14:30 # XIV:XXX
roman time from XIV:XXX # 14:30
# Latin words
roman words 42 # quadraginta duo
roman words from "duo milia" # 2000
# Fractions
roman uncia 0.5 # S (semis)
roman uncia from S # 0.5
roman uncia list # all 12 entries
Flags: --lowercase / -l, --json, --12h, --seconds, --version, --help.
TypeScript Support
Full type definitions ship with the package — no @types/ installation needed:
import type {
ToRomanOptions,
RomanTimeOptions,
ParsedTime,
BreakdownPart,
UnciaEntry,
TimeObject,
} from '@tushar_rayamajhi/roman_converter'
const opts: ToRomanOptions = { lowercase: true }
const timeOpts: RomanTimeOptions = { format: '12h', seconds: true, meridiem: true }
const parts: BreakdownPart[] = breakdown('MCMXCIV')
// Each part: { numeral: string, value: number }
const info: UnciaEntry = unciaInfo('S')
// { twelfths: number, symbol: string, name: string, decimal: number }
Try It Yourself
The package is live on npm and the source is on GitHub. Visit the interactive package page for a playground where you can try every feature directly in the browser — no installation needed.
- npm:
@tushar_rayamajhi/roman_converter - GitHub: tusharrayamajhi/roman-to-decimal
- Live playground: /packages/roman-converter
npm install @tushar_rayamajhi/roman_converter