Editing Validation

Validation

Schema-based validation with Zod and custom row rules.

NuGrid supports schema-based validation using any library that implements the Standard Schema specification, including Zod.

Interactive Demo

Basic Usage with Zod

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

// Define your schema
const userSchema = z.object({
  name: z.string().min(2, 'Name must be at least 2 characters'),
  email: z.string().email('Please enter a valid email'),
  age: z.number().int().min(18, 'Must be 18 or older').max(120),
  salary: z.number().min(0, 'Salary must be positive'),
})
</script>

<template>
  <NuGrid
    :data="data"
    :columns="columns"
    :validation="{ schema: userSchema }"
    :editing="{ enabled: true }"
  />
</template>

Validation Options

Configure validation behavior with an options object:

<script setup lang="ts">
const validationOptions = {
  schema: userSchema,
  validateOn: 'reward',      // When to validate
  showErrors: 'always',      // When to show error messages
  icon: 'i-lucide-alert-circle',
  onInvalid: 'block',        // What to do on invalid input
}
</script>

<template>
  <NuGrid
    :data="data"
    :columns="columns"
    :validation="validationOptions"
    :editing="{ enabled: true }"
  />
</template>

Options Reference

OptionTypeDefaultDescription
schemaStandardSchema-Validation schema (Zod, Valibot, etc.)
validateOn'submit' | 'blur' | 'change' | 'reward''reward'When to trigger validation
showErrors'never' | 'hover' | 'always''always'When to display error messages
iconstring'i-lucide-alert-circle'Error icon
onInvalid'block' | 'revert' | 'warn''block'Behavior on invalid input

Validate On Options

  • submit: Validate when pressing Enter or Tab
  • blur: Validate when the cell loses focus
  • change: Validate on every keystroke (real-time)
  • reward: "Reward early, punish late" - show success immediately, delay showing errors

On Invalid Options

  • block: Keep the cell in edit mode until valid
  • revert: Restore the original value and exit edit mode
  • warn: Allow navigation but keep the error indicator

Row-Level Validation

Add cross-field validation rules:

<script setup lang="ts">
const rowRules = [
  // Engineering department requires salary >= $100k
  (row: User) => {
    if (row.department === 'Engineering' && row.salary < 100000) {
      return {
        valid: false,
        message: 'Engineering employees must have salary >= $100,000',
        failedFields: ['salary'],
      }
    }
    return { valid: true }
  },
  // Manager role requires age >= 25
  (row: User) => {
    if (row.role === 'Manager' && row.age < 25) {
      return {
        valid: false,
        message: 'Managers must be at least 25 years old',
        failedFields: ['age'],
      }
    }
    return { valid: true }
  },
]

const validationOptions = {
  schema: userSchema,
  rowRules,
}
</script>

Complete Example

<script setup lang="ts">
import type { NuGridColumn, NuGridValidationOptions } from '#nu-grid/types'
import { z } from 'zod'

interface User {
  id: number
  name: string
  email: string
  age: number
  department: string
  salary: number
}

const data = ref<User[]>([
  { id: 1, name: 'Alice', email: 'alice@example.com', age: 28, department: 'Engineering', salary: 95000 },
  { id: 2, name: 'Bob', email: 'bob@example.com', age: 35, department: 'Marketing', salary: 65000 },
])

// Zod schema
const userSchema = z.object({
  id: z.number(),
  name: z.string().min(2).max(50),
  email: z.string().email(),
  age: z.number().int().min(18).max(120),
  department: z.string().min(1),
  salary: z.number().min(0),
})

// Row validation rules
const rowRules = [
  (row: User) => {
    if (row.department === 'Engineering' && row.salary < 100000) {
      return {
        valid: false,
        message: 'Engineering salary must be >= $100k',
        failedFields: ['salary'],
      }
    }
    return { valid: true }
  },
]

const validationOptions: NuGridValidationOptions<User> = {
  schema: userSchema,
  rowRules,
  validateOn: 'reward',
  showErrors: 'always',
  onInvalid: 'block',
}

const columns: NuGridColumn<User>[] = [
  { accessorKey: 'id', header: 'ID', enableEditing: false },
  { accessorKey: 'name', header: 'Name' },
  { accessorKey: 'email', header: 'Email' },
  { accessorKey: 'age', header: 'Age' },
  { accessorKey: 'department', header: 'Department' },
  {
    accessorKey: 'salary',
    header: 'Salary',
    cell: ({ row }) => `$${row.original.salary.toLocaleString()}`,
  },
]
</script>

<template>
  <NuGrid
    :data="data"
    :columns="columns"
    :validation="validationOptions"
    :editing="{ enabled: true, startClicks: 'double' }"
    :focus="{ retain: true }"
  />
</template>
Zod 4.x and later versions natively implement the Standard Schema v1 specification, so they work directly with NuGrid without any adapters.