username-schema
EditCanonicalizes and validates a URL-friendly username with branded output.
import * as z from "zod";
const USERNAME_MIN_LENGTH = 3;const USERNAME_MAX_LENGTH = 30;const USERNAME_PATTERN = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;const codePointLength = (value: string): number => Array.from(value).length;
/** * Validates and brands a canonical lowercase username. * * Usernames contain ASCII letters, numbers, and single dot, underscore, or * hyphen separators between alphanumeric groups. */export const usernameSchema = z .string({ error: "Username must be a string" }) .check( z.trim(), z.toLowerCase(), z.normalize("NFC"), z.refine( (value) => codePointLength(value) >= USERNAME_MIN_LENGTH, { error: `Username must contain at least ${USERNAME_MIN_LENGTH} characters`, }, ), z.refine( (value) => codePointLength(value) <= USERNAME_MAX_LENGTH, { error: `Username must contain at most ${USERNAME_MAX_LENGTH} characters`, }, ), z.regex(USERNAME_PATTERN, { error: "Username must use letters or numbers separated by single dots, underscores, or hyphens", }), ) .brand<"Username">() .meta({ title: "Username", description: "A canonical lowercase username containing 3 to 30 ASCII characters.", examples: ["person_42"], });
export type Username = z.infer<typeof usernameSchema>;Download
Section titled “Download”wget -O src/schemas/username-schema.ts https://raw.githubusercontent.com/jrTilak/lazykit/HEAD/registry/zod-schemas/username-schema.tstemp_dir="$(mktemp -d)" && bunx degit jrTilak/lazykit/registry/zod-schemas "$temp_dir" && mv "$temp_dir/username-schema.ts" src/schemas/username-schema.ts && rm -r "$temp_dir"Examples
Section titled “Examples”import { usernameSchema, type Username } from "./username-schema";
const username = usernameSchema.parse(" Ada.Lovelace ");
const openProfile = (handle: Username) => { console.log(`/users/${handle}`);};
openProfile(username);// Output: /users/ada.lovelaceinput(string) — The candidate username.
Returns
Section titled “Returns”Username, a canonical string branded with "Username". The schema trims
outer whitespace, lowercases the value, normalizes it to Unicode NFC, and then
requires 3 to 30 Unicode code points.
The final value contains lowercase ASCII letters or numbers separated by single dots, underscores, or hyphens. Separators cannot be repeated or appear at either edge. Availability and reserved-name checks remain application-level concerns.