Skip to main content

Validation

Validation

Runtime prop validation utilities for development

Tags: utility validation development

The theme library includes runtime validation utilities that provide helpful warnings during development when components receive invalid props.

Overview​

These utilities:

  • Only run in development mode
  • Are tree-shaken in production builds
  • Log warnings to console
  • Return validation results for conditional logic

Import​

<script>
  import {
    validateRequired,
    validateNonEmptyArray,
    validateOneOf,
    validateRange,
    validateProps,
    createValidator,
  } from '@classic-homes/theme-svelte';
</script>

Validation Functions​

validateRequired​

Check that a required prop is defined:

validateRequired(value, 'propName', 'ComponentName');

Example:

<script>
  let { id } = $props();

  $effect(() => {
    validateRequired(id, 'id', 'FormField');
  });
</script>

Output (if undefined):

[FormField] "id" is required

validateNonEmptyArray​

Check that an array prop has items:

validateNonEmptyArray(array, 'propName', 'ComponentName');

Example:

<script>
  let { options } = $props();

  $effect(() => {
    validateNonEmptyArray(options, 'options', 'Select');
  });
</script>

Output (if empty):

[Select] "options" should not be empty

validateOneOf​

Check that a value is one of allowed options:

validateOneOf(value, allowedValues, 'propName', 'ComponentName');

Example:

<script>
  let { variant = 'default' } = $props();

  $effect(() => {
    validateOneOf(
      variant,
      ['default', 'secondary', 'outline', 'ghost', 'destructive'],
      'variant',
      'Button'
    );
  });
</script>

Output (if invalid):

[Button] "variant" must be one of: default, secondary, outline, ghost, destructive. Got: invalid

validateRange​

Check that a number is within bounds:

validateRange(value, min, max, 'propName', 'ComponentName');

Example:

<script>
  let { progress = 0 } = $props();

  $effect(() => {
    validateRange(progress, 0, 100, 'progress', 'ProgressBar');
  });
</script>

Output (if out of range):

[ProgressBar] "progress" must be between 0 and 100. Got: 150

Combining Validations​

validateProps​

Check multiple validations at once:

const isValid = validateProps([
  validateRequired(id, 'id', 'Component'),
  validateNonEmptyArray(items, 'items', 'Component'),
  validateOneOf(variant, ['a', 'b'], 'variant', 'Component'),
]);

Example:

<script>
  let { id, options, size } = $props();

  $effect(() => {
    validateProps([
      validateRequired(id, 'id', 'Select'),
      validateNonEmptyArray(options, 'options', 'Select'),
      validateOneOf(size, ['sm', 'md', 'lg'], 'size', 'Select'),
    ]);
  });
</script>

createValidator​

Create a bound validator for cleaner code:

const validate = createValidator('ComponentName');

validate.required(value, 'propName');
validate.nonEmptyArray(array, 'propName');
validate.oneOf(value, allowedValues, 'propName');
validate.range(number, min, max, 'propName');

Example:

<script>
  import { createValidator } from '@classic-homes/theme-svelte';

  let { id, options, variant, step } = $props();

  const validate = createValidator('CustomSelect');

  $effect(() => {
    validate.required(id, 'id');
    validate.nonEmptyArray(options, 'options');
    validate.oneOf(variant, ['default', 'outline'], 'variant');
    validate.range(step, 1, 10, 'step');
  });
</script>

Validation Results​

All functions return a result object:

interface ValidationResult {
  valid: boolean;
  message?: string;
}

Use for conditional logic:

<script>
  $effect(() => {
    const result = validateRequired(value, 'value', 'Input');
    if (!result.valid) {
      // Handle invalid state
      showError(result.message);
    }
  });
</script>

Production Behavior​

In production builds, all validation functions:

  • Return { valid: true } immediately
  • Don't log any warnings
  • Are tree-shaken if unused

This is achieved through the esm-env package's DEV constant.

Best Practices​

  1. Validate in $effect: Run validations reactively

    $effect(() => {
      validateRequired(prop, 'prop', 'Component');
    });
  2. Use createValidator for consistency: Cleaner component code

    const validate = createValidator('MyComponent');
  3. Validate early: Catch issues at component init

    $effect(() => {
      // Runs immediately and on prop changes
      validate.nonEmptyArray(items, 'items');
    });
  4. Group related validations: Use validateProps for multiple checks

    validateProps([validate.required(id, 'id'), validate.required(label, 'label')]);

Example: Custom Component​

<script lang="ts">
  import { createValidator, validateProps } from '@classic-homes/theme-svelte';

  interface Props {
    id: string;
    options: { value: string; label: string }[];
    value?: string;
    size?: 'sm' | 'md' | 'lg';
    disabled?: boolean;
  }

  let { id, options, value = $bindable(''), size = 'md', disabled = false }: Props = $props();

  const validate = createValidator('CustomSelect');

  $effect(() => {
    validateProps([
      validate.required(id, 'id'),
      validate.nonEmptyArray(options, 'options'),
      validate.oneOf(size, ['sm', 'md', 'lg'], 'size'),
    ]);
  });
</script>
On this page