Skip to content

username-schema

Edit

Canonicalizes and validates a URL-friendly username with branded output.

username-schema.ts
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>;
Terminal
wget -O src/schemas/username-schema.ts https://raw.githubusercontent.com/jrTilak/lazykit/HEAD/registry/zod-schemas/username-schema.ts
username-schema.example.ts
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.lovelace
  • input (string) — The candidate username.

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.