Skip to content

@macrulez/vue-form-schema

vue-virtual-scroller-kit

Реактивные формы из декларативной схемы (JSON, Zod, Yup или Valibot) для Vue 3. Headless, SSR-совместимая альтернатива VeeValidate / FormKit для форм, которые генерируются динамически или управляются с сервера.

Возможности

  • Любой источник схемыFieldDefinition[], JSON-массив, Zod, Yup, Valibot или стандартная JSON Schema / OpenAPI
  • Headless по умолчанию — никаких UI-зависимостей в ядре; подключайте свои компоненты
  • Реактивные условияvisible, disabled принимают булево значение, функцию или строковое выражение
  • Динамические опции — синхронные и асинхронные функции options с отслеживанием зависимостей (optionsDeps)
  • Динамические массивы полейtype: 'array' с composable useFieldArray (append / remove / move / swap)
  • Многошаговый визардuseMultiStepForm с валидацией на каждом шаге и MultiStepFormRenderer
  • Валидация — синхронные и асинхронные валидаторы, validateMode: 'first' | 'all', validateOn: 'eager'
  • Кросс-полевая валидация — валидатор sameAs; валидаторы получают все текущие значения вторым аргументом
  • Transform и parsetransform запускается на каждом setField; parse — во время отправки
  • Загрузка файловtype: 'file' с валидаторами fileType, fileSize, fileCount; drag-and-drop UI
  • Кастомные компонентыfield.component + реестр компонентов на уровне приложения и поддерева
  • Маскирование ввода — телефон (RU/EU), дата, IBAN, ИНН, кастомные паттерны #/A; без внешних зависимостей
  • Композиция схемmergeSchemas, omitFields, pickFields, extendField
  • Дискриминированные схемыdiscriminatedFields строит visible-связку для набора полей, который целиком переключается по значению дискриминатора, с нативным маппингом z.discriminatedUnion / v.variant
  • Вывод типов TypeScriptInferValues<T> отображает литералы схемы в типизированные значения
  • Персистентные формыpersist: 'local' | 'session' с SSR-безопасным хранением
  • Ошибки валидации с сервераapplyServerErrors отображает форматы Laravel/DRF/flat/кастомный на errors
  • Режим отладкиdebug: true логирует изменения состояния; useFormDebug возвращает реактивный снапшот
  • Тема Tailwind UI — подточка входа vue-form-schema/ui/tailwind с компонентами на utility-классах
  • Темы shadcn / PrimeVue / Naive UI — готовые рендереры для популярных библиотек компонентов
  • Доступностьaria-required, aria-invalid, aria-describedby, fieldset/legend для radio
  • SSR-безопасность — никаких прямых обращений к browser API в ядре
  • Tree-shakeable — адаптеры Zod/Yup/Valibot и UI — отдельные точки входа
  • Модуль Nuxt@macrulez/nuxt-vue-form-schema автоимпортирует composables, валидаторы и адаптеры схем
  • Vue DevToolsvue-form-schema/devtools добавляет живой инспектор форм + таймлайн, нулевая цена, если не установлен

Установка

bash
npm install @macrulez/vue-form-schema

Опциональные peer-зависимости:

bash
npm install zod       # адаптер Zod
npm install yup       # адаптер Yup
npm install valibot   # адаптер Valibot

Используете Nuxt?

@macrulez/nuxt-vue-form-schema автоимпортирует useForm, useFieldArray, встроенные валидаторы, адаптеры схем и многое другое — ручной import не нужен:

bash
npm install @macrulez/nuxt-vue-form-schema
ts
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@macrulez/nuxt-vue-form-schema'],
})

Опции см. на странице Модуль Nuxt.

Быстрый старт

vue
<script setup lang="ts">
import { useForm } from '@macrulez/vue-form-schema'
import type { FieldDefinition } from '@macrulez/vue-form-schema'

const schema: FieldDefinition[] = [
  { type: 'text', name: 'name', label: 'Full name', required: true },
  { type: 'email', name: 'email', label: 'Email', required: true },
  {
    type: 'select',
    name: 'role',
    label: 'Role',
    options: [
      { label: 'Admin', value: 'admin' },
      { label: 'User', value: 'user' },
    ],
  },
]

const { values, errors, touched, isValid, isSubmitting, submit, setField } = useForm({
  schema,
  validateOn: 'blur',
  onSubmit: async (data) => {
    await fetch('/api/users', { method: 'POST', body: JSON.stringify(data) })
  },
})
</script>

<template>
  <form @submit.prevent="submit">
    <div v-for="field in schema" :key="field.name">
      <label>{{ field.label }}</label>
      <input
        :type="field.type"
        :value="values[field.name]"
        @input="setField(field.name, ($event.target as HTMLInputElement).value)"
        @blur="touched[field.name] = true"
      />
      <span v-if="touched[field.name] && errors[field.name]">
        {{ errors[field.name][0] }}
      </span>
    </div>
    <button type="submit" :disabled="!isValid || isSubmitting">Submit</button>
  </form>
</template>

Либо используйте FormRenderer для рендеринга без разметки:

vue
<script setup lang="ts">
import { useForm } from '@macrulez/vue-form-schema'
import { FormRenderer } from '@macrulez/vue-form-schema/ui'

const form = useForm({ schema, onSubmit })
</script>

<template>
  <FormRenderer :form="form" submit-label="Save" />
</template>