Skip to content
masterclassBuddyTech Buzz5 fresh blogs · 5:00 AM IST

Zod · Function

z.custom

z.custom() creates a Zod schema for a TypeScript type that lacks a built-in Zod schema. Supply a predicate for runtime validation and an optional custom error message; without a predicate, it accepts any value.

How it works

z.custom() creates a Zod schema for a TypeScript type that is not covered by a built-in schema. An optional predicate decides whether an input is accepted, and an optional second argument customizes the validation error; omitting the predicate performs no validation.

Syntax

z.custom<...>((val) => ..., "custom error message")

Parameters

NameTypeDescription
TTypeScript typeOptional TypeScript generic describing the accepted value type; the documentation represents it as ... in the signature.
check(val) => booleanOptional validation predicate receiving the value and returning whether it is valid.
messagestringOptional custom validation error message.

Returns

A Zod schema for the specified TypeScript type.

Key points

  • Use z.custom() when the desired TypeScript type is not represented by one of Zod’s built-in schemas.
  • It is suited to third-party-library types and other domain types without a built-in Zod schema.
  • Pass a generic type, a predicate, and optionally a custom message to express both the intended type and runtime rule.
  • Failures from z.custom() use the custom error category rather than the more descriptive invalid_format category.
  • Prefer specialized built-ins when validating class instances or template-literal types instead of reaching for z.custom().

Common mistakes

  • Calling z.custom() without a predicate accepts every value, so it should not be used when runtime validation is required.
  • Use z.instanceof() for class instances and z.templateLiteral() for template literal types when those built-in choices fit the requirement.

Example: Validate a template-literal-like pixel value

import { z } from "zod";

const Pixel = z.custom<`${number}px`>(
  (value) => typeof value === "string" && /^\d+px$/.test(value),
  "Expected a pixel value"
);

console.log(Pixel.parse("42px"));
console.log(Pixel.safeParse("42vw").success);

The predicate accepts strings consisting of digits followed by px; the second argument supplies the custom validation message.

When to use it

Validate external-library types

A third-party library exposes a branded or structural type that Zod has no dedicated schema for.

Encode domain-specific constraints

A domain value follows a custom string or structural rule not covered by built-in schemas.

Create a schema for unsupported types

A TypeScript type requires a schema for parsing or inference but has no direct Zod counterpart.

Interview questions

QWhat problem does z.custom() solve?

z.custom() defines a schema for a TypeScript type that Zod does not otherwise cover. Its predicate receives the input and must determine whether validation succeeds.

Follow-up: When should a different API be preferred? Use z.instanceof() for class instances and z.templateLiteral() for template literal types.

QHow do static typing and runtime checking work together in z.custom()?

The generic parameter describes the TypeScript type inferred from the schema, while the predicate supplies runtime validation. For example, a predicate can check that a value matches a string pattern.

Follow-up: What happens if the predicate is omitted? The schema performs no runtime validation and accepts any value.

QHow can z.custom() errors be customized?

A second argument can provide a custom error message for failed validation. This makes failures identify the intended domain-specific constraint instead of relying only on a generic message.

Follow-up: How could class-instance validation be made more idiomatic? Replace z.custom() with z.instanceof() when the target is a class instance.

  • z.instanceof()
  • z.templateLiteral()
  • z.refine()
  • z.infer

Source: Defining schemas | Zod. Checked 2026-10-15.