Skip to content

VeeValidate v5 Bridge

VeeValidate v5 accepts Standard Schema-compatible validators as validationSchema.

That means json-rest-schema does not need a heavy VeeValidate-specific runtime adapter. This package now ships a small bridge that wraps a schema instance in the Standard Schema interface VeeValidate already understands.

Import it like this:

javascript
import { useForm } from 'vee-validate'
import { createSchema } from 'json-rest-schema'
import { toVeeValidateSchema } from 'json-rest-schema/vee-validate'

Basic usage

javascript
const profileSchema = createSchema({
  name: { type: 'string', required: true, minLength: 3 },
  role: { type: 'string', defaultTo: 'guest' }
})

const { handleSubmit, errors, values } = useForm({
  initialValues: {
    name: ''
  },
  validationSchema: toVeeValidateSchema(profileSchema)
})

That default bridge uses create semantics.

So on successful submit:

  • required fields are enforced
  • normalized values are returned
  • defaults are applied to the submitted output

If the user submits:

javascript
{
  name: '  Alex  '
}

then the validated submit payload is equivalent to:

javascript
{
  name: 'Alex',
  role: 'guest'
}

Edit forms and custom operations

If the form is editing an existing resource, pass the operation explicitly:

javascript
const { handleSubmit } = useForm({
  initialValues,
  validationSchema: toVeeValidateSchema(profileSchema, {
    operation: 'patch'
  })
})

Custom operations work too:

javascript
const { handleSubmit } = useForm({
  initialValues,
  validationSchema: toVeeValidateSchema(profileSchema, {
    operation: 'upsert'
  })
})

Important VeeValidate limitation: defaults do not initialize form state

This is important enough to say clearly:

  • the bridge validates and normalizes the schema output
  • VeeValidate still expects you to provide your own initialValues
  • schema defaults do not automatically populate the form's starting state

So this is the intended split:

  • initialValues controls the raw form state
  • toVeeValidateSchema(...) controls validation and normalized submit output

If you want a default field visible in the UI before submit, put it in initialValues.

If you only want the normalized payload to contain the default when the user submits, let the schema apply it.

Error paths

The bridge turns the library's flat error map into Standard Schema issues with nested paths.

So an internal error like:

javascript
{
  'roles.0.label': {
    field: 'roles.0.label',
    code: 'REQUIRED',
    message: 'Field is required',
    params: {}
  }
}

becomes Standard Schema issues equivalent to:

javascript
[
  {
    message: 'Field is required',
    path: ['roles', 0, 'label']
  }
]

That is what lets VeeValidate map nested array/object errors back onto the right field state.

Worked VeeValidate example

javascript
import { useForm } from 'vee-validate'
import { createSchema } from 'json-rest-schema'
import { toVeeValidateSchema } from 'json-rest-schema/vee-validate'

const workspaceSummarySchema = createSchema({
  id: { type: 'id', required: true },
  slug: { type: 'string', required: true, minLength: 3 },
  ownerUserId: { type: 'id', required: true }
})

const workspaceSchema = createSchema({
  workspace: {
    type: 'object',
    required: true,
    schema: workspaceSummarySchema
  }
})

const { defineField, handleSubmit, errors } = useForm({
  initialValues: {
    workspace: {
      slug: ''
    }
  },
  validationSchema: toVeeValidateSchema(workspaceSchema, {
    operation: 'patch'
  })
})

const [slug, slugAttrs] = defineField('workspace.slug')

const saveWorkspace = handleSubmit((validatedObject) => {
  return api.saveWorkspace(validatedObject)
})
vue
<template>
  <form @submit.prevent="saveWorkspace">
    <input v-model="slug" v-bind="slugAttrs">
    <span>{{ errors['workspace.slug'] }}</span>
    <button type="submit">Save</button>
  </form>
</template>

This keeps the responsibilities clean:

  • VeeValidate owns touched/dirty/submit orchestration
  • json-rest-schema owns validation and normalization
  • your submit handler owns business logic

GPL-3.0-only