Skip to main content

Schemas (Standard Schema)

Every identifier has a ready-made Standard Schema. Standard Schema is the common interface that Zod, Valibot, ArkType and others implement, and that form and API libraries accept directly. dr-utils implements it with zero dependencies.

Use schemas when a library asks you for "a schema". Use the plain validators everywhere else: they are unchanged and still the simplest option.

SchemaAcceptsOutput (on success)
cedulaSchema(options?)cédula, dashes and spaces allowedCedula — 11 digits
rncSchema(options?)RNC or cédula, dashes and spaces allowedRNC — 9 or 11 digits
ncfSchema(options?)NCF / e-CF, surrounding whitespace okNCF
phoneNumberSchema(options?)any common phone format, +1 optionalPhoneNumber — 10 digits
plateSchema(options?)license plate, any casePlate — uppercase
provinciaSchema(options?)province name, any case or accentsProvincia — official name
municipioSchema(options?)municipality name, any case or accentsMunicipio — official name

Options

OptionTypeDescription
messagestringError message for invalid input. Defaults to English ('Invalid cedula').

Schemas validate synchronously and also reject non-string values.

With TanStack Form​

TanStack Form accepts Standard Schemas as field validators:

import { useForm } from '@tanstack/react-form'
import { cedulaSchema, phoneNumberSchema } from 'dr-utils/schemas'

const form = useForm({ defaultValues: { cedula: '', telefono: '' } })

<form.Field
name="cedula"
validators={{ onChange: cedulaSchema({ message: 'Cédula inválida' }) }}
children={(field) => (
<>
<input value={field.state.value} onChange={(e) => field.handleChange(e.target.value)} />
<p>{field.state.meta.errors[0]?.message}</p>
</>
)}
/>

With any Standard Schema consumer​

Any library that accepts a Standard Schema for a single value works the same way, for example a tRPC procedure input: .input(rncSchema()). You can also call a schema yourself:

import { rncSchema } from 'dr-utils/schemas'

const result = rncSchema()['~standard'].validate('130-72075-4')

if (result.issues) {
console.log(result.issues[0].message) // 'Invalid RNC'
} else {
console.log(result.value) // '130720754'
}

Inside Zod or Valibot objects​

Zod and Valibot can't nest another library's schema inside their own objects. Use the validator functions with their refinement helpers instead; it's the same check:

import { z } from 'zod'
import { validateCedula, validatePhoneNumber } from 'dr-utils/validators'
import { normalizePhoneNumber } from 'dr-utils/parsers'

const Cliente = z.object({
cedula: z.string().refine(validateCedula, 'Cédula inválida'),
telefono: z
.string()
.refine(validatePhoneNumber, 'Teléfono inválido')
.transform(normalizePhoneNumber), // '(809) 555-1234' → '8095551234'
})
import * as v from 'valibot'
import { validateRNC } from 'dr-utils/validators'

const Empresa = v.object({
rnc: v.pipe(v.string(), v.check(validateRNC, 'RNC inválido')),
})

Types​

import { cedulaSchema, type StandardSchemaV1 } from 'dr-utils'

type Output = StandardSchemaV1.InferOutput<ReturnType<typeof cedulaSchema>> // Cedula