Custom Schema
Schema is the core of Velite. It defines the structure and type of your content and validates it.
Refer to Velite Schemas for more information about built-in schema.
Velite supports custom schema. A schema is a JavaScript function that returns a Zod schema object.
Generally, I divide the schema into two categories: one for data validation and the other for data transformation.
Define a Validation Schema
import { defineSchema, s } from 'velite'
// `s` is extended from Zod with some custom schemas,
// `s` also includes all members of zod, so you can use `s` as `z`
// for validating title
export const title = defineSchema(() => s.string().min(1).max(100))
// for validating email
export const email = defineSchema(() => s.string().email({ message: 'Invalid email address' }))
// custom validation logic
export const hello = defineSchema(() =>
s.string().refine(value => {
if (value !== 'hello') {
return 'Value must be "hello"'
}
return true
})
)Refer to Zod documentation for more information about Zod.
Define a Transformation Schema
import { defineSchema, s } from 'velite'
// for transforming title
export const title = defineSchema(() => s.string().transform(value => value.toUpperCase()))
// ...Example
Remote Image with BlurDataURL Schema
import { getImageMetadata, s } from 'velite'
import type { Image } from 'velite'
/**
* Remote Image with metadata schema
*/
export const remoteImage = () =>
s.string().transform<Image>(async (value, { addIssue }) => {
try {
const response = await fetch(value)
const blob = await response.blob()
const buffer = await blob.arrayBuffer()
const metadata = await getImageMetadata(Buffer.from(buffer))
if (metadata == null) throw new Error(`Failed to get image metadata: ${value}`)
return { src: value, ...metadata }
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
addIssue({ fatal: true, code: 'custom', message })
return null as never
}
})Schema Context
TIP
Custom schemas often need to read the current file or resolved config. For new schemas, use context() to access this parser context.
import { context, defineSchema, s } from 'velite'
// convert a nonexistent field
export const path = defineSchema(() =>
s.custom<string | undefined>().transform(value => {
if (value != null) return value
return context().file.path
})
)context() must be called while Velite is parsing a schema, such as inside .transform(), .refine(), or .superRefine(). It returns the current parser context:
interface ParserContext {
readonly config: Config
readonly file: VeliteFile
}The previous schema callback meta value is still supported for compatibility:
import { defineSchema, s } from 'velite'
export const path = defineSchema(() =>
s.custom<string | undefined>().transform((value, { meta }) => {
if (value != null) return value
return meta.path
})
)Prefer context() for new custom schemas. It keeps file/config access explicit and avoids relying on Velite's extended Zod callback metadata.
Reference
The type of context().file is VeliteFile. The type of the compatibility meta value is ZodMeta, which extends VeliteFile.