Skip to content

Validator

A lightweight inline validation wrapper for individual form elements. Unlike Form + FormItem which requires a full form model, PxValidator wraps any input and shows pixel-styled hint text when Zod validation fails. Inspired by DaisyUI's Validator pattern.

NameCategoryDescriptionType
rules? BehaviorValidation rules (supports Zod schema or required shorthand)ValidatorRule[]
modelValue? ContentThe value to validateunknown
validate? EventTriggered after validation completes(isValid: boolean, message: string) => void
default? SlotForm control content
hint? SlotCustom error hint content{ message: string }
validate? ExposeManually trigger validation(trigger?: string) => Promise<boolean>
reset? ExposeReset validation state() => void
validateStatus? ExposeCurrent validation statusRef<'init' | 'error' | 'success'>
validateMessage? ExposeCurrent error messageRef<string>

Basic Usage

Wrap a form element with PxValidator, bind v-model and provide rules. Use required: true as a shorthand for non-empty string validation.

<template>
  <div class="demo-validator">
    <px-validator v-model="name" :rules="rules">
      <px-input v-model="name" placeholder="Enter your name" />
    </px-validator>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const name = ref('');
const rules = [{ required: true, trigger: 'change' }];
</script>

<style scoped>
.demo-validator {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 320px;
}
</style>

Zod Schema Validation

Use Zod schemas for rich validation — email format, minimum length, regex patterns, and more.

<template>
  <div class="demo-validator">
    <px-validator v-model="email" :rules="emailRules">
      <px-input v-model="email" placeholder="Email address" />
    </px-validator>
    <px-validator v-model="password" :rules="passwordRules">
      <px-input v-model="password" type="password" placeholder="Password (min 6 chars)" />
    </px-validator>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { z } from 'zod';

const email = ref('');
const password = ref('');

const emailRules = [{ schema: z.string().email('Please enter a valid email'), trigger: 'change' }];
const passwordRules = [
  { schema: z.string().min(6, 'Password must be at least 6 characters'), trigger: 'change' },
];
</script>

<style scoped>
.demo-validator {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 320px;
}
</style>

Custom Error Message

Override Zod's default error with a custom message in the rule.

<template>
  <div class="demo-validator">
    <px-validator v-model="age" :rules="rules">
      <px-input v-model="age" placeholder="Enter your age" />
    </px-validator>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { z } from 'zod';

const age = ref('');
const rules = [
  {
    schema: z.string().regex(/^\d+$/).pipe(z.coerce.number().min(1).max(150)),
    message: 'Please enter a valid age (1-150)',
    trigger: 'change',
  },
];
</script>

<style scoped>
.demo-validator {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 320px;
}
</style>

Trigger Modes

Control when validation runs using trigger: 'change', 'blur', or 'input'.

<template>
  <div class="demo-validator">
    <p class="demo-validator__label">Validates on change:</p>
    <px-validator v-model="val1" :rules="[{ required: true, trigger: 'change' }]">
      <px-input v-model="val1" placeholder="Type and clear..." />
    </px-validator>

    <p class="demo-validator__label">Validates on blur:</p>
    <px-validator v-model="val2" :rules="[{ required: true, trigger: 'blur' }]">
      <px-input v-model="val2" placeholder="Focus and leave empty..." />
    </px-validator>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const val1 = ref('');
const val2 = ref('');
</script>

<style scoped>
.demo-validator {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 320px;
}
.demo-validator__label {
  font-family: var(--px-font-family);
  font-size: 14px;
  color: var(--px-text-color-secondary);
  margin: 0;
}
</style>

Programmatic Validation

Access validate() and reset() via template ref for manual control.

<template>
  <div class="demo-validator">
    <px-validator ref="validatorRef" v-model="username" :rules="rules">
      <px-input v-model="username" placeholder="Username" />
    </px-validator>
    <div class="demo-validator__actions">
      <px-button type="primary" @click="handleValidate">Validate</px-button>
      <px-button @click="handleReset">Reset</px-button>
    </div>
    <p class="demo-validator__result">
      Status: {{ validatorRef?.validateStatus ?? 'init' }}
    </p>
  </div>
</template>

<script setup lang="ts">
import type { ValidatorInstance } from 'sakana-element';
import { ref } from 'vue';
import { z } from 'zod';

const username = ref('');
const validatorRef = ref<ValidatorInstance>();

const rules = [{ schema: z.string().min(3, 'Username must be at least 3 characters') }];

const handleValidate = async () => {
  try {
    await validatorRef.value?.validate();
  } catch {
    // validation failed
  }
};

const handleReset = () => {
  validatorRef.value?.reset();
};
</script>

<style scoped>
.demo-validator {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 320px;
}
.demo-validator__actions {
  display: flex;
  gap: 8px;
}
.demo-validator__result {
  font-family: var(--px-font-family);
  font-size: 14px;
  color: var(--px-text-color-secondary);
  margin: 0;
}
</style>