Skip to content

React Example

Complete React examples with rut.ts.

Simple RUT Input Component#

TypeScript
import { useState } from 'react'
import { format, validate } from 'rut.ts'
 
export function RutInput() {
  const [rut, setRut] = useState('')
  const [isValid, setIsValid] = useState(false)
  
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const formatted = format(e.target.value, { incremental: true })
    setRut(formatted)
    setIsValid(validate(formatted))
  }
  
  return (
    <div>
      <input
        type="text"
        value={rut}
        onChange={handleChange}
        placeholder="12.345.678-5"
        className={isValid ? 'valid' : 'invalid'}
      />
      {!isValid && rut && (
        <span className="error">Invalid RUT</span>
      )}
    </div>
  )
}

Controlled Component with Validation#

TypeScript
import { useState, useEffect } from 'react'
import { format, validate, clean } from 'rut.ts'
 
interface RutInputProps {
  value?: string
  onChange?: (cleanedRut: string) => void
  onValidChange?: (isValid: boolean) => void
  strictMode?: boolean
}
 
export function RutInput({ 
  value = '', 
  onChange, 
  onValidChange,
  strictMode = false 
}: RutInputProps) {
  const [displayValue, setDisplayValue] = useState('')
  const [error, setError] = useState<string | null>(null)
  
  useEffect(() => {
    if (value) {
      const formatted = format(value, { 
        incremental: true,
        throwOnError: false 
      })
      if (formatted) setDisplayValue(formatted)
    }
  }, [value])
  
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const input = e.target.value
    
    // Format incrementally
    const formatted = format(input, { incremental: true })
    setDisplayValue(formatted)
    
    // Validate
    const isValid = validate(formatted, { strict: strictMode })
    
    if (isValid) {
      setError(null)
      const cleaned = clean(formatted)
      onChange?.(cleaned)
      onValidChange?.(true)
    } else {
      setError('Invalid RUT')
      onValidChange?.(false)
    }
  }
  
  return (
    <div className="rut-input">
      <input
        type="text"
        value={displayValue}
        onChange={handleChange}
        placeholder="12.345.678-5"
        className={error ? 'error' : ''}
      />
      {error && <span className="error-message">{error}</span>}
    </div>
  )
}

Form with Multiple Fields#

TypeScript
import { useState } from 'react'
import { validate, clean, format } from 'rut.ts'
 
interface UserForm {
  name: string
  rut: string
  email: string
}
 
export function UserRegistration() {
  const [form, setForm] = useState<UserForm>({
    name: '',
    rut: '',
    email: ''
  })
  const [errors, setErrors] = useState<Partial<UserForm>>({})
  
  const handleRutChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const formatted = format(e.target.value, { incremental: true })
    setForm(prev => ({ ...prev, rut: formatted }))
    
    // Clear error if valid
    if (validate(formatted)) {
      setErrors(prev => ({ ...prev, rut: undefined }))
    }
  }
  
  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    
    // Validate RUT
    if (!validate(form.rut)) {
      setErrors(prev => ({ ...prev, rut: 'Invalid RUT' }))
      return
    }
    
    // Clean RUT for API
    const cleanedRut = clean(form.rut)
    
    // Submit
    await fetch('/api/users', {
      method: 'POST',
      body: JSON.stringify({ ...form, rut: cleanedRut })
    })
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Name:</label>
        <input
          value={form.name}
          onChange={(e) => setForm(prev => ({ ...prev, name: e.target.value }))}
        />
      </div>
      
      <div>
        <label>RUT:</label>
        <input
          value={form.rut}
          onChange={handleRutChange}
          placeholder="12.345.678-5"
        />
        {errors.rut && <span className="error">{errors.rut}</span>}
      </div>
      
      <div>
        <label>Email:</label>
        <input
          type="email"
          value={form.email}
          onChange={(e) => setForm(prev => ({ ...prev, email: e.target.value }))}
        />
      </div>
      
      <button type="submit">Register</button>
    </form>
  )
}

Display Formatted RUT#

TypeScript
import { format } from 'rut.ts'
 
interface RutDisplayProps {
  rut: string
  withDots?: boolean
}
 
export function RutDisplay({ rut, withDots = true }: RutDisplayProps) {
  const formatted = format(rut, { 
    dots: withDots,
    throwOnError: false 
  })
  
  if (!formatted) {
    return <span className="error">Invalid RUT</span>
  }
  
  return <span className="rut">{formatted}</span>
}
 
// Usage
<RutDisplay rut="123456785" />           // '12.345.678-5'
<RutDisplay rut="123456785" withDots={false} />  // '12345678-5'

Searchable RUT Table#

TypeScript
import { useState, useMemo } from 'react'
import { clean, format, isRutLike } from 'rut.ts'
 
interface User {
  id: number
  name: string
  rut: string
}
 
export function UserTable({ users }: { users: User[] }) {
  const [search, setSearch] = useState('')
  
  const filteredUsers = useMemo(() => {
    if (!search) return users
    
    // Clean search term for comparison
    const searchCleaned = clean(search, { throwOnError: false })
    if (!searchCleaned) return users
    
    return users.filter(user => {
      const userCleaned = clean(user.rut, { throwOnError: false })
      return userCleaned?.includes(searchCleaned)
    })
  }, [users, search])
  
  return (
    <div>
      <input
        type="text"
        value={search}
        onChange={(e) => setSearch(e.target.value)}
        placeholder="Search by RUT"
      />
      
      <table>
        <thead>
          <tr>
            <th>Name</th>
            <th>RUT</th>
          </tr>
        </thead>
        <tbody>
          {filteredUsers.map(user => (
            <tr key={user.id}>
              <td>{user.name}</td>
              <td>{format(user.rut)}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  )
}