\n\n\n```\n\n## Implementación para Node.js/Backend\n\n### Módulo CommonJS:\n\n```javascript\n// rut-validator.js\nmodule.exports = {\n /**\n * Valida un RUT chileno\n * @param {string} rut - RUT a validar\n * @returns {object} Resultado de la validación\n */\n validate: function(rut) {\n try {\n const cleaned = this.clean(rut);\n \n if (!cleaned || cleaned.length < 2) {\n return {\n isValid: false,\n error: 'RUT muy corto'\n };\n }\n \n const number = cleaned.slice(0, -1);\n const dv = cleaned.slice(-1);\n \n if (!/^d+$/.test(number)) {\n return {\n isValid: false,\n error: 'RUT contiene caracteres inválidos'\n };\n }\n \n const calculatedDv = this.calculateDv(number);\n const isValid = dv === calculatedDv;\n \n return {\n isValid,\n formatted: isValid ? this.format(cleaned) : null,\n error: isValid ? null : 'Dígito verificador incorrecto'\n };\n } catch (error) {\n return {\n isValid: false,\n error: error.message\n };\n }\n },\n \n clean: function(rut) {\n return String(rut).replace(/[.-s]/g, '').toUpperCase();\n },\n \n calculateDv: function(rutNumber) {\n const rut = String(rutNumber).padStart(8, '0');\n let sum = 0;\n let multiplier = 2;\n \n for (let i = rut.length - 1; i >= 0; i--) {\n sum += parseInt(rut[i]) * multiplier;\n multiplier = multiplier === 7 ? 2 : multiplier + 1;\n }\n \n const dv = 11 - (sum % 11);\n if (dv === 11) return '0';\n if (dv === 10) return 'K';\n return String(dv);\n },\n \n format: function(rut) {\n const cleaned = this.clean(rut);\n if (cleaned.length < 2) return cleaned;\n \n const number = cleaned.slice(0, -1);\n const dv = cleaned.slice(-1);\n \n let formatted = '';\n for (let i = number.length - 1, j = 0; i >= 0; i--, j++) {\n if (j > 0 && j % 3 === 0) {\n formatted = '.' + formatted;\n }\n formatted = number[i] + formatted;\n }\n \n return formatted + '-' + dv;\n }\n};\n```\n\n### Módulo ES6:\n\n```javascript\n// rut-validator.mjs\nexport class ChileanRutValidator {\n static #factors = [2, 3, 4, 5, 6, 7];\n \n static clean(rut) {\n return String(rut).replace(/[.-s]/g, '').toUpperCase();\n }\n \n static calculateCheckDigit(rutNumber) {\n const digits = String(rutNumber)\n .padStart(8, '0')\n .split('')\n .reverse()\n .map(Number);\n \n const sum = digits.reduce((acc, digit, index) => {\n const factor = this.#factors[index % 6];\n return acc + (digit * factor);\n }, 0);\n \n const checkDigit = 11 - (sum % 11);\n \n switch (checkDigit) {\n case 11: return '0';\n case 10: return 'K';\n default: return String(checkDigit);\n }\n }\n \n static validate(rut) {\n const cleaned = this.clean(rut);\n \n if (cleaned.length < 2) {\n throw new Error('RUT demasiado corto');\n }\n \n const [number, checkDigit] = [\n cleaned.slice(0, -1),\n cleaned.slice(-1)\n ];\n \n if (!/^d+$/.test(number)) {\n throw new Error('RUT contiene caracteres no numéricos');\n }\n \n return checkDigit === this.calculateCheckDigit(number);\n }\n \n static format(rut) {\n const cleaned = this.clean(rut);\n if (cleaned.length < 2) return cleaned;\n \n const [number, checkDigit] = [\n cleaned.slice(0, -1),\n cleaned.slice(-1)\n ];\n \n const formatted = number\n .split('')\n .reverse()\n .reduce((acc, digit, index) => {\n if (index > 0 && index % 3 === 0) {\n acc = '.' + acc;\n }\n return digit + acc;\n }, '');\n \n return `${formatted}-${checkDigit}`;\n }\n}\n\n// Uso\nimport { ChileanRutValidator } from './rut-validator.mjs';\n\nconsole.log(ChileanRutValidator.validate('12345678-5')); // true\nconsole.log(ChileanRutValidator.format('123456785')); // 12.345.678-5\n```\n\n## Integración con frameworks populares\n\n### React Hook personalizado:\n\n```javascript\n// useRutValidator.js\nimport { useState, useCallback } from 'react';\n\nconst useRutValidator = () => {\n const [rut, setRut] = useState('');\n const [isValid, setIsValid] = useState(null);\n const [formatted, setFormatted] = useState('');\n \n const clean = useCallback((value) => {\n return String(value).replace(/[.-s]/g, '').toUpperCase();\n }, []);\n \n const calculateDv = useCallback((rutNumber) => {\n const rut = String(rutNumber).padStart(8, '0');\n let sum = 0;\n let multiplier = 2;\n \n for (let i = rut.length - 1; i >= 0; i--) {\n sum += parseInt(rut[i]) * multiplier;\n multiplier = multiplier === 7 ? 2 : multiplier + 1;\n }\n \n const dv = 11 - (sum % 11);\n if (dv === 11) return '0';\n if (dv === 10) return 'K';\n return String(dv);\n }, []);\n \n const format = useCallback((value) => {\n const cleaned = clean(value);\n if (cleaned.length < 2) return cleaned;\n \n const number = cleaned.slice(0, -1);\n const dv = cleaned.slice(-1);\n \n const formatted = number.replace(/B(?=(d{3})+(?!d))/g, '.');\n return `${formatted}-${dv}`;\n }, [clean]);\n \n const validate = useCallback((value) => {\n const cleaned = clean(value);\n \n if (cleaned.length < 2) return false;\n \n const number = cleaned.slice(0, -1);\n const dv = cleaned.slice(-1);\n \n if (!/^d+$/.test(number)) return false;\n \n return dv === calculateDv(number);\n }, [clean, calculateDv]);\n \n const handleChange = useCallback((value) => {\n setRut(value);\n \n const cleaned = clean(value);\n if (cleaned.length >= 8) {\n const valid = validate(value);\n setIsValid(valid);\n setFormatted(valid ? format(value) : '');\n } else {\n setIsValid(null);\n setFormatted('');\n }\n }, [clean, validate, format]);\n \n return {\n rut,\n isValid,\n formatted,\n handleChange,\n validate,\n format,\n clean\n };\n};\n\n// Componente React de ejemplo\nconst RutInput = () => {\n const { rut, isValid, formatted, handleChange } = useRutValidator();\n \n return (\n
\n handleChange(e.target.value)}\n placeholder=\"Ingrese RUT\"\n className={isValid === null ? '' : isValid ? 'valid' : 'invalid'}\n />\n {isValid !== null && (\n

{isValid ? `✓ RUT válido: ${formatted}` : '✗ RUT inválido'}

\n )}\n
\n );\n};\n```\n\n### Vue.js Composable:\n\n```javascript\n// useRutValidator.js\nimport { ref, computed } from 'vue';\n\nexport function useRutValidator() {\n const rut = ref('');\n \n const clean = (value) => {\n return String(value).replace(/[.-s]/g, '').toUpperCase();\n };\n \n const calculateDv = (rutNumber) => {\n const rutStr = String(rutNumber).padStart(8, '0');\n let sum = 0;\n let multiplier = 2;\n \n for (let i = rutStr.length - 1; i >= 0; i--) {\n sum += parseInt(rutStr[i]) * multiplier;\n multiplier = multiplier === 7 ? 2 : multiplier + 1;\n }\n \n const dv = 11 - (sum % 11);\n if (dv === 11) return '0';\n if (dv === 10) return 'K';\n return String(dv);\n };\n \n const isValid = computed(() => {\n const cleaned = clean(rut.value);\n if (cleaned.length < 8) return null;\n \n const number = cleaned.slice(0, -1);\n const dv = cleaned.slice(-1);\n \n if (!/^d+$/.test(number)) return false;\n \n return dv === calculateDv(number);\n });\n \n const formatted = computed(() => {\n if (!isValid.value) return '';\n \n const cleaned = clean(rut.value);\n const number = cleaned.slice(0, -1);\n const dv = cleaned.slice(-1);\n \n const formattedNumber = number.replace(/B(?=(d{3})+(?!d))/g, '.');\n return `${formattedNumber}-${dv}`;\n });\n \n return {\n rut,\n isValid,\n formatted,\n clean,\n calculateDv\n };\n}\n```\n\n## Testing con Jest\n\n### Suite de pruebas completa:\n\n```javascript\n// rut-validator.test.js\nconst RutValidator = require('./rut-validator');\n\ndescribe('RutValidator', () => {\n describe('clean', () => {\n test('removes dots, dashes and spaces', () => {\n expect(RutValidator.clean('12.345.678-9')).toBe('123456789');\n expect(RutValidator.clean('12 345 678-9')).toBe('123456789');\n expect(RutValidator.clean('12345678-k')).toBe('12345678K');\n });\n });\n \n describe('calculateDv', () => {\n test('calculates correct check digit', () => {\n expect(RutValidator.calculateDv(12345678)).toBe('5');\n expect(RutValidator.calculateDv(11111111)).toBe('1');\n expect(RutValidator.calculateDv(22222222)).toBe('2');\n });\n \n test('returns 0 when result is 11', () => {\n expect(RutValidator.calculateDv(15834660)).toBe('0');\n });\n \n test('returns K when result is 10', () => {\n expect(RutValidator.calculateDv(15834661)).toBe('K');\n });\n });\n \n describe('validate', () => {\n test('validates correct RUTs', () => {\n expect(RutValidator.validate('12.345.678-5')).toBe(true);\n expect(RutValidator.validate('11111111-1')).toBe(true);\n expect(RutValidator.validate('5.126.663-3')).toBe(true);\n });\n \n test('rejects incorrect RUTs', () => {\n expect(RutValidator.validate('12.345.678-9')).toBe(false);\n expect(RutValidator.validate('11111111-2')).toBe(false);\n });\n \n test('handles edge cases', () => {\n expect(RutValidator.validate('')).toBe(false);\n expect(RutValidator.validate('1')).toBe(false);\n expect(RutValidator.validate('abc')).toBe(false);\n });\n });\n \n describe('format', () => {\n test('formats RUT correctly', () => {\n expect(RutValidator.format('123456785')).toBe('12.345.678-5');\n expect(RutValidator.format('11111111')).toBe('1.111.111-1');\n expect(RutValidator.format('51266633')).toBe('5.126.663-3');\n });\n });\n});\n```\n\n## Optimizaciones y mejores prácticas\n\n### Versión optimizada con memoización:\n\n```javascript\nclass OptimizedRutValidator {\n static #cache = new Map();\n static #maxCacheSize = 1000;\n \n static validate(rut) {\n const cleaned = this.clean(rut);\n \n // Verificar cache\n if (this.#cache.has(cleaned)) {\n return this.#cache.get(cleaned);\n }\n \n // Validar\n const result = this.#performValidation(cleaned);\n \n // Guardar en cache con límite\n if (this.#cache.size >= this.#maxCacheSize) {\n const firstKey = this.#cache.keys().next().value;\n this.#cache.delete(firstKey);\n }\n this.#cache.set(cleaned, result);\n \n return result;\n }\n \n static #performValidation(cleaned) {\n if (cleaned.length < 2) return false;\n \n const number = cleaned.slice(0, -1);\n const dv = cleaned.slice(-1);\n \n if (!/^d+$/.test(number)) return false;\n \n // Optimización: usar lookup table para factores\n const factors = [2, 3, 4, 5, 6, 7];\n let sum = 0;\n \n for (let i = number.length - 1, j = 0; i >= 0; i--, j++) {\n sum += parseInt(number[i]) * factors[j % 6];\n }\n \n const calculatedDv = 11 - (sum % 11);\n const expectedDv = calculatedDv === 11 ? '0' : \n calculatedDv === 10 ? 'K' : \n String(calculatedDv);\n \n return dv === expectedDv;\n }\n \n static clean(rut) {\n return String(rut).replace(/[.-s]/g, '').toUpperCase();\n }\n \n static clearCache() {\n this.#cache.clear();\n }\n}\n```\n\n## Conclusión\n\nJavaScript ofrece la flexibilidad perfecta para implementar la validación de RUT chileno en cualquier contexto: desde simples validaciones en el navegador hasta complejos sistemas backend. El código presentado es robusto, eficiente y fácil de mantener.\n\nLas implementaciones mostradas cubren todos los casos de uso comunes: validación básica, formateo automático, integración con frameworks modernos y optimizaciones para alto rendimiento. Puedes usar estos ejemplos como base y adaptarlos según tus necesidades específicas.\n\nRecuerda siempre validar tanto en el cliente (mejor UX) como en el servidor (seguridad), y considera implementar caché para aplicaciones de alto tráfico.\n\n---\n\n### Recursos adicionales\n\n- **MDN Web Docs**: Documentación completa de JavaScript\n- **NPM**: Busca paquetes existentes de validación de RUT\n- **GitHub**: Encuentra más implementaciones y contribuye a proyectos open source\n","keywords":"JavaScript, RUT, programación, validación, módulo 11","articleSection":"Desarrollo"}
Volver al blog
Desarrollo

Validar RUT en JavaScript: Código Completo y Explicado

12 min de lectura
JavaScript
RUT
programación
validación
módulo 11

Validar RUT en JavaScript: Código Completo y Explicado

Por qué JavaScript es ideal para validar RUT

JavaScript es el lenguaje perfecto para validar RUTs chilenos en aplicaciones web. Permite validación instantánea en el navegador, mejora la experiencia del usuario al dar feedback inmediato, y evita enviar datos incorrectos al servidor. Además, el mismo código puede usarse tanto en el frontend como en el backend con Node.js.

En este tutorial, aprenderás a implementar el algoritmo módulo 11 de forma profesional, con código limpio, optimizado y listo para producción.

Implementación básica del algoritmo

Función esencial para calcular el dígito verificador:

JavaScript
1function calcularDigitoVerificador(rutNumero) {
2  // Convertir a string y asegurar que tenga 8 dígitos
3  let rut = String(rutNumero).padStart(8, '0');
4  
5  // Variables para el cálculo
6  let suma = 0;
7  let multiplicador = 2;
8  
9  // Recorrer de derecha a izquierda
10  for (let i = rut.length - 1; i >= 0; i--) {
11    suma += parseInt(rut[i]) * multiplicador;
12    multiplicador = multiplicador === 7 ? 2 : multiplicador + 1;
13  }
14  
15  // Calcular dígito verificador
16  const resto = suma % 11;
17  const dv = 11 - resto;
18  
19  // Casos especiales
20  if (dv === 11) return '0';
21  if (dv === 10) return 'K';
22  return String(dv);
23}
24
25// Ejemplos de uso
26console.log(calcularDigitoVerificador(12345678)); // 5
27console.log(calcularDigitoVerificador(11111111)); // 1
28console.log(calcularDigitoVerificador(22222222)); // 2

Validador completo con manejo de formatos

Clase profesional para manejar RUTs:

JavaScript
1class RutValidator {
2  /**
3   * Limpia el RUT removiendo puntos, guiones y espacios
4   * @param {string} rut - RUT en cualquier formato
5   * @returns {string} RUT limpio
6   */
7  static limpiar(rut) {
8    return String(rut).replace(/[.-s]/g, '').toUpperCase();
9  }
10
11  /**
12   * Formatea el RUT con puntos y guión
13   * @param {string} rut - RUT sin formato
14   * @returns {string} RUT formateado (ej: 12.345.678-9)
15   */
16  static formatear(rut) {
17    const rutLimpio = this.limpiar(rut);
18    if (rutLimpio.length < 2) return rutLimpio;
19    
20    const cuerpo = rutLimpio.slice(0, -1);
21    const dv = rutLimpio.slice(-1);
22    
23    // Agregar puntos cada 3 dígitos de derecha a izquierda
24    let cuerpoFormateado = '';
25    for (let i = cuerpo.length - 1, j = 0; i >= 0; i--, j++) {
26      if (j > 0 && j % 3 === 0) {
27        cuerpoFormateado = '.' + cuerpoFormateado;
28      }
29      cuerpoFormateado = cuerpo[i] + cuerpoFormateado;
30    }
31    
32    return `${cuerpoFormateado}-${dv}`;
33  }
34
35  /**
36   * Calcula el dígito verificador
37   * @param {string|number} rutNumero - Número del RUT sin DV
38   * @returns {string} Dígito verificador
39   */
40  static calcularDV(rutNumero) {
41    const rut = String(rutNumero).padStart(8, '0');
42    let suma = 0;
43    let multiplicador = 2;
44    
45    for (let i = rut.length - 1; i >= 0; i--) {
46      suma += parseInt(rut[i]) * multiplicador;
47      multiplicador = multiplicador === 7 ? 2 : multiplicador + 1;
48    }
49    
50    const dv = 11 - (suma % 11);
51    if (dv === 11) return '0';
52    if (dv === 10) return 'K';
53    return String(dv);
54  }
55
56  /**
57   * Valida un RUT completo
58   * @param {string} rut - RUT completo con o sin formato
59   * @returns {boolean} true si es válido
60   */
61  static validar(rut) {
62    const rutLimpio = this.limpiar(rut);
63    
64    // Validar largo mínimo
65    if (rutLimpio.length < 2) return false;
66    
67    // Separar número y dígito verificador
68    const numero = rutLimpio.slice(0, -1);
69    const dvIngresado = rutLimpio.slice(-1);
70    
71    // Validar que sea un número
72    if (!/^d+$/.test(numero)) return false;
73    
74    // Calcular DV esperado y comparar
75    const dvCalculado = this.calcularDV(numero);
76    return dvIngresado === dvCalculado;
77  }
78}
79
80// Ejemplos de uso
81console.log(RutValidator.validar('12.345.678-5')); // true
82console.log(RutValidator.validar('12345678-5'));   // true
83console.log(RutValidator.validar('123456785'));    // true
84console.log(RutValidator.validar('12.345.678-9')); // false

Implementación moderna con ES6+

Versión funcional con arrow functions:

JavaScript
1const rutUtils = {
2  // Limpiar RUT
3  clean: (rut) => String(rut).replace(/[.-s]/g, '').toUpperCase(),
4  
5  // Calcular dígito verificador
6  calculateDV: (rutNumber) => {
7    const digits = String(rutNumber).padStart(8, '0').split('').reverse();
8    const sum = digits.reduce((acc, digit, index) => {
9      const factor = ((index % 6) + 2);
10      return acc + (parseInt(digit) * factor);
11    }, 0);
12    
13    const dv = 11 - (sum % 11);
14    return dv === 11 ? '0' : dv === 10 ? 'K' : String(dv);
15  },
16  
17  // Validar RUT completo
18  validate: (rut) => {
19    const cleaned = rutUtils.clean(rut);
20    if (cleaned.length < 2) return false;
21    
22    const number = cleaned.slice(0, -1);
23    const dv = cleaned.slice(-1);
24    
25    return /^d+$/.test(number) && dv === rutUtils.calculateDV(number);
26  },
27  
28  // Formatear RUT
29  format: (rut) => {
30    const cleaned = rutUtils.clean(rut);
31    if (cleaned.length < 2) return cleaned;
32    
33    const number = cleaned.slice(0, -1);
34    const dv = cleaned.slice(-1);
35    
36    // Formatear con regex
37    const formatted = number.replace(/B(?=(d{3})+(?!d))/g, '.');
38    return `${formatted}-${dv}`;
39  }
40};
41
42// Uso con destructuring
43const { validate, format, calculateDV } = rutUtils;
44
45console.log(validate('12345678-5'));  // true
46console.log(format('123456785'));     // 12.345.678-5
47console.log(calculateDV(12345678));   // 5

Validación en formularios HTML

Integración con formularios web:

HTML
1<!DOCTYPE html>
2<html lang="es">
3<head>
4    <meta charset="UTF-8">
5    <title>Validador de RUT</title>
6    <style>
7        .form-group {
8            margin: 20px 0;
9        }
10        
11        .form-control {
12            padding: 10px;
13            font-size: 16px;
14            border: 2px solid #ddd;
15            border-radius: 4px;
16            width: 300px;
17        }
18        
19        .form-control.is-valid {
20            border-color: #28a745;
21        }
22        
23        .form-control.is-invalid {
24            border-color: #dc3545;
25        }
26        
27        .feedback {
28            margin-top: 5px;
29            font-size: 14px;
30        }
31        
32        .valid-feedback {
33            color: #28a745;
34        }
35        
36        .invalid-feedback {
37            color: #dc3545;
38        }
39    </style>
40</head>
41<body>
42    <div class="form-group">
43        <label for="rut">Ingrese su RUT:</label>
44        <input 
45            type="text" 
46            id="rut" 
47            class="form-control" 
48            placeholder="12.345.678-9"
49            maxlength="12"
50        >
51        <div class="feedback"></div>
52    </div>
53    
54    <div class="form-group">
55        <label for="rut-auto">RUT con formato automático:</label>
56        <input 
57            type="text" 
58            id="rut-auto" 
59            class="form-control" 
60            placeholder="Ingrese RUT sin formato"
61        >
62        <div class="feedback"></div>
63    </div>
64
65    <script>
66        // Clase RutValidator (incluir el código anterior aquí)
67        
68        // Validación en tiempo real
69        document.getElementById('rut').addEventListener('input', function(e) {
70            const rut = e.target.value;
71            const feedback = e.target.nextElementSibling;
72            
73            if (rut.length === 0) {
74                e.target.classList.remove('is-valid', 'is-invalid');
75                feedback.textContent = '';
76                return;
77            }
78            
79            if (RutValidator.validar(rut)) {
80                e.target.classList.remove('is-invalid');
81                e.target.classList.add('is-valid');
82                feedback.className = 'feedback valid-feedback';
83                feedback.textContent = '✓ RUT válido';
84            } else {
85                e.target.classList.remove('is-valid');
86                e.target.classList.add('is-invalid');
87                feedback.className = 'feedback invalid-feedback';
88                feedback.textContent = '✗ RUT inválido';
89            }
90        });
91        
92        // Formato automático mientras escribe
93        document.getElementById('rut-auto').addEventListener('input', function(e) {
94            const valor = e.target.value;
95            const limpio = RutValidator.limpiar(valor);
96            
97            // Solo números y K
98            if (!/^[dkK]*$/.test(limpio)) {
99                e.target.value = e.target.value.slice(0, -1);
100                return;
101            }
102            
103            // Formatear automáticamente
104            if (limpio.length > 1) {
105                e.target.value = RutValidator.formatear(limpio);
106            }
107            
108            // Validar
109            const feedback = e.target.nextElementSibling;
110            if (limpio.length >= 8) {
111                if (RutValidator.validar(limpio)) {
112                    e.target.classList.remove('is-invalid');
113                    e.target.classList.add('is-valid');
114                    feedback.className = 'feedback valid-feedback';
115                    feedback.textContent = '✓ RUT válido';
116                } else {
117                    e.target.classList.remove('is-valid');
118                    e.target.classList.add('is-invalid');
119                    feedback.className = 'feedback invalid-feedback';
120                    feedback.textContent = '✗ RUT inválido';
121                }
122            } else {
123                e.target.classList.remove('is-valid', 'is-invalid');
124                feedback.textContent = '';
125            }
126        });
127    </script>
128</body>
129</html>

Implementación para Node.js/Backend

Módulo CommonJS:

JavaScript
1// rut-validator.js
2module.exports = {
3  /**
4   * Valida un RUT chileno
5   * @param {string} rut - RUT a validar
6   * @returns {object} Resultado de la validación
7   */
8  validate: function(rut) {
9    try {
10      const cleaned = this.clean(rut);
11      
12      if (!cleaned || cleaned.length < 2) {
13        return {
14          isValid: false,
15          error: 'RUT muy corto'
16        };
17      }
18      
19      const number = cleaned.slice(0, -1);
20      const dv = cleaned.slice(-1);
21      
22      if (!/^d+$/.test(number)) {
23        return {
24          isValid: false,
25          error: 'RUT contiene caracteres inválidos'
26        };
27      }
28      
29      const calculatedDv = this.calculateDv(number);
30      const isValid = dv === calculatedDv;
31      
32      return {
33        isValid,
34        formatted: isValid ? this.format(cleaned) : null,
35        error: isValid ? null : 'Dígito verificador incorrecto'
36      };
37    } catch (error) {
38      return {
39        isValid: false,
40        error: error.message
41      };
42    }
43  },
44  
45  clean: function(rut) {
46    return String(rut).replace(/[.-s]/g, '').toUpperCase();
47  },
48  
49  calculateDv: function(rutNumber) {
50    const rut = String(rutNumber).padStart(8, '0');
51    let sum = 0;
52    let multiplier = 2;
53    
54    for (let i = rut.length - 1; i >= 0; i--) {
55      sum += parseInt(rut[i]) * multiplier;
56      multiplier = multiplier === 7 ? 2 : multiplier + 1;
57    }
58    
59    const dv = 11 - (sum % 11);
60    if (dv === 11) return '0';
61    if (dv === 10) return 'K';
62    return String(dv);
63  },
64  
65  format: function(rut) {
66    const cleaned = this.clean(rut);
67    if (cleaned.length < 2) return cleaned;
68    
69    const number = cleaned.slice(0, -1);
70    const dv = cleaned.slice(-1);
71    
72    let formatted = '';
73    for (let i = number.length - 1, j = 0; i >= 0; i--, j++) {
74      if (j > 0 && j % 3 === 0) {
75        formatted = '.' + formatted;
76      }
77      formatted = number[i] + formatted;
78    }
79    
80    return formatted + '-' + dv;
81  }
82};

Módulo ES6:

JavaScript
1// rut-validator.mjs
2export class ChileanRutValidator {
3  static #factors = [2, 3, 4, 5, 6, 7];
4  
5  static clean(rut) {
6    return String(rut).replace(/[.-s]/g, '').toUpperCase();
7  }
8  
9  static calculateCheckDigit(rutNumber) {
10    const digits = String(rutNumber)
11      .padStart(8, '0')
12      .split('')
13      .reverse()
14      .map(Number);
15    
16    const sum = digits.reduce((acc, digit, index) => {
17      const factor = this.#factors[index % 6];
18      return acc + (digit * factor);
19    }, 0);
20    
21    const checkDigit = 11 - (sum % 11);
22    
23    switch (checkDigit) {
24      case 11: return '0';
25      case 10: return 'K';
26      default: return String(checkDigit);
27    }
28  }
29  
30  static validate(rut) {
31    const cleaned = this.clean(rut);
32    
33    if (cleaned.length < 2) {
34      throw new Error('RUT demasiado corto');
35    }
36    
37    const [number, checkDigit] = [
38      cleaned.slice(0, -1),
39      cleaned.slice(-1)
40    ];
41    
42    if (!/^d+$/.test(number)) {
43      throw new Error('RUT contiene caracteres no numéricos');
44    }
45    
46    return checkDigit === this.calculateCheckDigit(number);
47  }
48  
49  static format(rut) {
50    const cleaned = this.clean(rut);
51    if (cleaned.length < 2) return cleaned;
52    
53    const [number, checkDigit] = [
54      cleaned.slice(0, -1),
55      cleaned.slice(-1)
56    ];
57    
58    const formatted = number
59      .split('')
60      .reverse()
61      .reduce((acc, digit, index) => {
62        if (index > 0 && index % 3 === 0) {
63          acc = '.' + acc;
64        }
65        return digit + acc;
66      }, '');
67    
68    return `${formatted}-${checkDigit}`;
69  }
70}
71
72// Uso
73import { ChileanRutValidator } from './rut-validator.mjs';
74
75console.log(ChileanRutValidator.validate('12345678-5')); // true
76console.log(ChileanRutValidator.format('123456785'));    // 12.345.678-5

Integración con frameworks populares

React Hook personalizado:

JavaScript
1// useRutValidator.js
2import { useState, useCallback } from 'react';
3
4const useRutValidator = () => {
5  const [rut, setRut] = useState('');
6  const [isValid, setIsValid] = useState(null);
7  const [formatted, setFormatted] = useState('');
8  
9  const clean = useCallback((value) => {
10    return String(value).replace(/[.-s]/g, '').toUpperCase();
11  }, []);
12  
13  const calculateDv = useCallback((rutNumber) => {
14    const rut = String(rutNumber).padStart(8, '0');
15    let sum = 0;
16    let multiplier = 2;
17    
18    for (let i = rut.length - 1; i >= 0; i--) {
19      sum += parseInt(rut[i]) * multiplier;
20      multiplier = multiplier === 7 ? 2 : multiplier + 1;
21    }
22    
23    const dv = 11 - (sum % 11);
24    if (dv === 11) return '0';
25    if (dv === 10) return 'K';
26    return String(dv);
27  }, []);
28  
29  const format = useCallback((value) => {
30    const cleaned = clean(value);
31    if (cleaned.length < 2) return cleaned;
32    
33    const number = cleaned.slice(0, -1);
34    const dv = cleaned.slice(-1);
35    
36    const formatted = number.replace(/B(?=(d{3})+(?!d))/g, '.');
37    return `${formatted}-${dv}`;
38  }, [clean]);
39  
40  const validate = useCallback((value) => {
41    const cleaned = clean(value);
42    
43    if (cleaned.length < 2) return false;
44    
45    const number = cleaned.slice(0, -1);
46    const dv = cleaned.slice(-1);
47    
48    if (!/^d+$/.test(number)) return false;
49    
50    return dv === calculateDv(number);
51  }, [clean, calculateDv]);
52  
53  const handleChange = useCallback((value) => {
54    setRut(value);
55    
56    const cleaned = clean(value);
57    if (cleaned.length >= 8) {
58      const valid = validate(value);
59      setIsValid(valid);
60      setFormatted(valid ? format(value) : '');
61    } else {
62      setIsValid(null);
63      setFormatted('');
64    }
65  }, [clean, validate, format]);
66  
67  return {
68    rut,
69    isValid,
70    formatted,
71    handleChange,
72    validate,
73    format,
74    clean
75  };
76};
77
78// Componente React de ejemplo
79const RutInput = () => {
80  const { rut, isValid, formatted, handleChange } = useRutValidator();
81  
82  return (
83    <div>
84      <input
85        type="text"
86        value={rut}
87        onChange={(e) => handleChange(e.target.value)}
88        placeholder="Ingrese RUT"
89        className={isValid === null ? '' : isValid ? 'valid' : 'invalid'}
90      />
91      {isValid !== null && (
92        <p>{isValid ? `✓ RUT válido: ${formatted}` : '✗ RUT inválido'}</p>
93      )}
94    </div>
95  );
96};

Vue.js Composable:

JavaScript
1// useRutValidator.js
2import { ref, computed } from 'vue';
3
4export function useRutValidator() {
5  const rut = ref('');
6  
7  const clean = (value) => {
8    return String(value).replace(/[.-s]/g, '').toUpperCase();
9  };
10  
11  const calculateDv = (rutNumber) => {
12    const rutStr = String(rutNumber).padStart(8, '0');
13    let sum = 0;
14    let multiplier = 2;
15    
16    for (let i = rutStr.length - 1; i >= 0; i--) {
17      sum += parseInt(rutStr[i]) * multiplier;
18      multiplier = multiplier === 7 ? 2 : multiplier + 1;
19    }
20    
21    const dv = 11 - (sum % 11);
22    if (dv === 11) return '0';
23    if (dv === 10) return 'K';
24    return String(dv);
25  };
26  
27  const isValid = computed(() => {
28    const cleaned = clean(rut.value);
29    if (cleaned.length < 8) return null;
30    
31    const number = cleaned.slice(0, -1);
32    const dv = cleaned.slice(-1);
33    
34    if (!/^d+$/.test(number)) return false;
35    
36    return dv === calculateDv(number);
37  });
38  
39  const formatted = computed(() => {
40    if (!isValid.value) return '';
41    
42    const cleaned = clean(rut.value);
43    const number = cleaned.slice(0, -1);
44    const dv = cleaned.slice(-1);
45    
46    const formattedNumber = number.replace(/B(?=(d{3})+(?!d))/g, '.');
47    return `${formattedNumber}-${dv}`;
48  });
49  
50  return {
51    rut,
52    isValid,
53    formatted,
54    clean,
55    calculateDv
56  };
57}

Testing con Jest

Suite de pruebas completa:

JavaScript
1// rut-validator.test.js
2const RutValidator = require('./rut-validator');
3
4describe('RutValidator', () => {
5  describe('clean', () => {
6    test('removes dots, dashes and spaces', () => {
7      expect(RutValidator.clean('12.345.678-9')).toBe('123456789');
8      expect(RutValidator.clean('12 345 678-9')).toBe('123456789');
9      expect(RutValidator.clean('12345678-k')).toBe('12345678K');
10    });
11  });
12  
13  describe('calculateDv', () => {
14    test('calculates correct check digit', () => {
15      expect(RutValidator.calculateDv(12345678)).toBe('5');
16      expect(RutValidator.calculateDv(11111111)).toBe('1');
17      expect(RutValidator.calculateDv(22222222)).toBe('2');
18    });
19    
20    test('returns 0 when result is 11', () => {
21      expect(RutValidator.calculateDv(15834660)).toBe('0');
22    });
23    
24    test('returns K when result is 10', () => {
25      expect(RutValidator.calculateDv(15834661)).toBe('K');
26    });
27  });
28  
29  describe('validate', () => {
30    test('validates correct RUTs', () => {
31      expect(RutValidator.validate('12.345.678-5')).toBe(true);
32      expect(RutValidator.validate('11111111-1')).toBe(true);
33      expect(RutValidator.validate('5.126.663-3')).toBe(true);
34    });
35    
36    test('rejects incorrect RUTs', () => {
37      expect(RutValidator.validate('12.345.678-9')).toBe(false);
38      expect(RutValidator.validate('11111111-2')).toBe(false);
39    });
40    
41    test('handles edge cases', () => {
42      expect(RutValidator.validate('')).toBe(false);
43      expect(RutValidator.validate('1')).toBe(false);
44      expect(RutValidator.validate('abc')).toBe(false);
45    });
46  });
47  
48  describe('format', () => {
49    test('formats RUT correctly', () => {
50      expect(RutValidator.format('123456785')).toBe('12.345.678-5');
51      expect(RutValidator.format('11111111')).toBe('1.111.111-1');
52      expect(RutValidator.format('51266633')).toBe('5.126.663-3');
53    });
54  });
55});

Optimizaciones y mejores prácticas

Versión optimizada con memoización:

JavaScript
1class OptimizedRutValidator {
2  static #cache = new Map();
3  static #maxCacheSize = 1000;
4  
5  static validate(rut) {
6    const cleaned = this.clean(rut);
7    
8    // Verificar cache
9    if (this.#cache.has(cleaned)) {
10      return this.#cache.get(cleaned);
11    }
12    
13    // Validar
14    const result = this.#performValidation(cleaned);
15    
16    // Guardar en cache con límite
17    if (this.#cache.size >= this.#maxCacheSize) {
18      const firstKey = this.#cache.keys().next().value;
19      this.#cache.delete(firstKey);
20    }
21    this.#cache.set(cleaned, result);
22    
23    return result;
24  }
25  
26  static #performValidation(cleaned) {
27    if (cleaned.length < 2) return false;
28    
29    const number = cleaned.slice(0, -1);
30    const dv = cleaned.slice(-1);
31    
32    if (!/^d+$/.test(number)) return false;
33    
34    // Optimización: usar lookup table para factores
35    const factors = [2, 3, 4, 5, 6, 7];
36    let sum = 0;
37    
38    for (let i = number.length - 1, j = 0; i >= 0; i--, j++) {
39      sum += parseInt(number[i]) * factors[j % 6];
40    }
41    
42    const calculatedDv = 11 - (sum % 11);
43    const expectedDv = calculatedDv === 11 ? '0' : 
44                      calculatedDv === 10 ? 'K' : 
45                      String(calculatedDv);
46    
47    return dv === expectedDv;
48  }
49  
50  static clean(rut) {
51    return String(rut).replace(/[.-s]/g, '').toUpperCase();
52  }
53  
54  static clearCache() {
55    this.#cache.clear();
56  }
57}

Conclusión

JavaScript ofrece la flexibilidad perfecta para implementar la validación de RUT chileno en cualquier contexto: desde simples validaciones en el navegador hasta complejos sistemas backend. El código presentado es robusto, eficiente y fácil de mantener.

Las implementaciones mostradas cubren todos los casos de uso comunes: validación básica, formateo automático, integración con frameworks modernos y optimizaciones para alto rendimiento. Puedes usar estos ejemplos como base y adaptarlos según tus necesidades específicas.

Recuerda siempre validar tanto en el cliente (mejor UX) como en el servidor (seguridad), y considera implementar caché para aplicaciones de alto tráfico.

---

Recursos adicionales

  • MDN Web Docs: Documentación completa de JavaScript
  • NPM: Busca paquetes existentes de validación de RUT
  • GitHub: Encuentra más implementaciones y contribuye a proyectos open source

Sobre el autor

DC

Diego Contreras

Desarrollador Frontend

Desarrollador frontend que vive entre JavaScript y TypeScript. Músico amateur y fanático de los asados domingueros.