Is this field required? Good luck finding out
TL;DR
Schema validators like Yup and Zod are great at defining validation logic - but terrible at exposing whether a field is required or optional (so form libraries also can't help). If you're building dynamic forms and want to mark required fields (e.g. with an asterisk), you'll quickly discover there's no easy, official way to extract that info. This post walks through the problem and explores practical workarounds like zod-to-json-schema
.
The hidden pain of required or optional fields in schema validation
Schema-based validators like Yup and Zod are powerful tools used widely in modern frontend forms. They provide a declarative and consistent way to express validation logic. But what they don’t provide - at least not easily - is a reliable way to answer a simple UI question:
“Is this field required?”
I ran into this issue while building a dynamic form. I wanted to automatically add an asterisk (*
) next to required field labels. Simple, right? But it turned out to be far from straightforward.
Why is this tricky?
The core issue is that neither Yup nor Zod provides a stable or documented way to extract metadata from a schema - such as whether a field is required or optional. These libraries are optimized for validation logic, not for form rendering or introspection.
Let’s look at simple examples:
Yup
import * as Yup from 'yup'
const schema = Yup.object({
username: Yup.string().required(),
email: Yup.string().optional(),
})
Zod
import { z } from 'zod'
const schema = z.object({
username: z.string(), // implicitly required
email: z.string().optional(),
})
As developers, it's obvious what's required. But programmatically extracting this information - especially from nested, conditional, or refined schemas - is difficult, if not impossible, without digging into undocumented internals.
This creates a tension: the schema knows what's required - but it won’t tell you.
Form libraries won’t save you
You might expect that form libraries like react-hook-form, Formik, or TanStack Form would help bridge this gap - but they don’t.
While these libraries integrate well with schema validators, they do not expose metadata like field requirement status. For example, using react-hook-form
with Zod:
<input {...register('username')} />
There’s no way to determine if username
is required - unless you duplicate that knowledge elsewhere. So developers often fall back on this:
<label>
Username *
<input {...register('username')} />
</label>
Which works - but completely breaks the idea of keeping validation and form rendering in sync. Any change in the schema can lead to inconsistencies in the UI unless manually updated in multiple places.
Workarounds and alternatives
Until introspection becomes a first-class feature in these libraries, here are two practical paths forward:
1. Custom schema parsing
You can manually inspect schema objects and try to infer which fields are required. For example, checking if .optional()
is present in Zod, or if a Yup chain includes .required()
.
This is technically possible, but comes with major downsides:
- Fragile: Relies on private/internal API structures that can break with library updates.
- Limited: Doesn’t work well for dynamic logic (e.g.
.refine()
,.superRefine()
, or conditional schemas). - Tedious: Not scalable across large or deeply nested schemas.
2. Use conversion tools
For Zod, a more maintainable workaround is to convert the schema to JSON Schema using zod-to-json-schema
. JSON Schema includes a required
array by design:
import { z } from 'zod'
import { zodToJsonSchema } from 'zod-to-json-schema'
const schema = z.object({
username: z.string(),
email: z.string().optional(),
})
const jsonSchema = zodToJsonSchema(schema)
console.log(jsonSchema.required) // ["username"]
Once converted, you can use this metadata to dynamically render labels, helper text, or accessibility hints.
Keep in mind: while this works well for many use cases, some Zod-specific features (like
.refine()
or brand types) may not translate perfectly into JSON Schema. It’s not a silver bullet - but it’s often good enough.
A pragmatic path forward
This limitation highlights a broader reality: schema validators are designed to validate - not to describe - data at runtime. That’s a key architectural trade-off.
While it would be ideal for form libraries or schema tools to expose requirement metadata directly, we’re not quite there yet. Until then, frontend developers must choose the most pragmatic strategy based on project needs:
- Use conversion tools like
zod-to-json-schema
where possible. - Define metadata separately when precise control is needed.
- Avoid deep reliance on internal structures that may change in future versions.
Being aware of these constraints helps you design forms that are robust, maintainable, and consistent - even in the absence of perfect tooling. So good luck with finding out if a field is required or not! :)