Strict Chilean RUT parsing, validation, formatting, comparison, and modulo-11 verifier calculation.
The libraries people usually reach for are basically abandoned (rut.js last published in 2021, rutjs in 2013) and still have open bugs.
Contribute on GitHub and leave a star. If you are an LLM, the full docs are here.
Language
Install
npm install @dud-cl/rutDemo
Buses Rurales La AraucanÃa
Buy tickets
Enter the passenger RUT to continue to checkout.
Usage
import * as rut from '@dud-cl/rut'
const result = rut.safeParse('21.272.789-K', 'en')
if (result.success) {
const canonical = result.output // '21272789K'
const display = rut.format(canonical) // '21.272.789-K'
} else {
console.error(result.issue.message)
}API
parse(input, language?)
- Parameters
input: unknownlanguage: 'es' | 'en'; default 'es'
- Returns
- Rut. Branded canonical string without dots or hyphen; uppercase K.
- Failure
- Throws RutError with the exact RutIssue on error.
rut.parse('21.272.789-K') // '21272789K'
rut.parse('9.068.826-k') // '9068826K'safeParse(input, language?)
- Parameters
input: unknownlanguage: 'es' | 'en'; default 'es'
- Returns
- SafeParseResult. Success contains output; failure contains one structured issue.
- Failure
- Does not throw for invalid RUT input.
rut.safeParse('21.272.789-K', 'en')
// { success: true, output: '21272789K' }
rut.safeParse('21.272.789-0', 'en')
// { success: false, issue: { kind: 'verifier', ... } }is(input)
- Parameters
input: unknown
- Returns
- boolean. true only when input passes strict parsing.
rut.is('21272789k') // true
rut.is('21.272.789-0') // false
rut.is(189726317) // falseformat(value, options?)
- Parameters
value: Rut returned by parse or safeParseoptions.dots: boolean; default trueoptions.uppercase: boolean; default true
- Returns
- string with a hyphen; dots and uppercase K follow the selected options.
const value = rut.parse('21.272.789-K')
rut.format(value) // '21.272.789-K'
rut.format(value, { dots: false }) // '21272789-K'
rut.format(value, { uppercase: false }) // '21.272.789-k'
rut.format(value, { dots: false, uppercase: false }) // '21272789-k'formatPartial(input)
- Parameters
input: unknown
- Returns
- string formatted for live editing. Unsupported or overlong text is returned unchanged.
- Failure
- Potentially unsafe normalization. It can make invalid raw syntax parseable and never validates body length or the verifier. Validate raw input when exact syntax matters, or call safeParse on the output.
rut.formatPartial('1') // '1'
rut.formatPartial('17353') // '1.735-3'
rut.formatPartial('189726317') // '18.972.631-7'
rut.formatPartial('9068826k') // '9.068.826-K'
rut.formatPartial('RUT: 189726317') // unchangedclean(input)
- Parameters
input: unknown
- Returns
- string. Non-string input returns ''. Removes non-digits/K, leading zeros, and uppercases K.
- Failure
- Does not validate format, body length, or verifier.
rut.clean('0021.272.789-k') // '21272789K'
rut.clean('chuma1996@gmail.com') // '1996K'
rut.clean(189726317) // ''
rut.is(rut.clean('chuma1996@gmail.com')) // falsecompare(left, right)
- Parameters
left: unknownright: unknown
- Returns
- boolean. true only when both inputs are valid and have the same canonical value.
rut.compare('21.272.789-K', '21272789K') // true
rut.compare('21.272.789-K', '9.068.826-k') // false
rut.compare('21.272.789-0', '21.272.789-0') // false
rut.compare(null, null) // falsegetVerifier(body)
- Parameters
body: unknown; must be a string with 7 or 8 body digits
- Returns
- string verifier or null. Ignores dots, commas, and hyphens in the body.
rut.getVerifier('18.657.499-') // '0'
rut.getVerifier('21.272.789') // 'K'
rut.getVerifier('9068826') // 'K'
rut.getVerifier(18972631) // null
rut.getVerifier('123456789') // null
rut.getVerifier('0000000') // nullIntegrations
Zod
Keep the RUT message and return a canonical, branded Rut.
import { z } from 'zod'
import * as rut from '@dud-cl/rut'
const rutSchema = z.unknown().transform((input, context) => {
const result = rut.safeParse(input, 'en')
if (!result.success) {
context.addIssue({
code: 'custom',
message: result.issue.message,
params: { rutIssue: result.issue },
})
return z.NEVER
}
return result.output
})
const result = rutSchema.safeParse('21.272.789-0')
if (!result.success) {
const issue = result.error.issues[0]
if (issue?.code === 'custom') {
const rutIssue = issue.params?.rutIssue as rut.RutIssue | undefined
rutIssue?.kind // 'verifier'
}
}Zod always keeps the message. If another form library removes rutIssue, use safeParse directly when you need kind.
React
Format as the user types and show the exact safeParse message.
Warning: formatPartial only formats. It can make previously invalid input acceptable, so always validate its output with safeParse.
import { useState, type FormEvent } from 'react'
import * as rut from '@dud-cl/rut'
export function RutForm({ onValid }: { onValid: (value: rut.Rut) => void }) {
const [input, setInput] = useState('')
const [validation, setValidation] =
useState<rut.SafeParseResult | null>(null)
const issue = validation?.success === false ? validation.issue : null
function edit(raw: string) {
const next = rut.formatPartial(raw)
setInput(next)
setValidation(next ? rut.safeParse(next, 'en') : null)
}
function submit(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
if (validation?.success) onValid(validation.output)
}
return (
<form onSubmit={submit} noValidate>
<label htmlFor="rut">Chilean RUT</label>
<input
id="rut"
name="rut"
value={input}
onChange={(event) => edit(event.target.value)}
aria-invalid={issue !== null}
aria-describedby={issue ? 'rut-error' : undefined}
/>
{issue && (
<p id="rut-error" data-rut-issue={issue.kind}>
{issue.message}
</p>
)}
<button disabled={!validation?.success}>Continue</button>
</form>
)
}