We are Coltor Apps ― a software development agency behind this OSS. Need a reliable tech partner?

Let’s talk

Guides

Form validation approaches

About this guide

This short guide demonstrates how to trigger entity validation in various ways to support any desired UX.

Validate on change

To validate an individual entity whenever its value changes, you can use the useEntityValueUpdated hook:

import {
  InterpreterEntities,
  useEntityValueUpdated,
  useInterpreterStore,
} from "@coltorapps/builder-react";

const components = { textField: InterpreterTextFieldEntity };

export function App() {
  const interpreterStore = useInterpreterStore(formBuilder, formSchema);

  useEntityValueUpdated(
    interpreterStore,
    (entity) => void interpreterStore.validateEntityValue(entity.id),
  );

  async function submitForm() {
    const validationResult = await interpreterStore.validateEntitiesValues();

    if (validationResult.success) {
      // Send to server.
    }
  }

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();

        void submitForm();
      }}
    >
      <InterpreterEntities
        interpreterStore={interpreterStore}
        components={components}
      />
      <button type="submit">Submit</button>
    </form>
  );
}

You can also trigger validation at the interpreter entity component level:

import {
  useEntityError,
  useEntityValue,
  type InterpreterEntityComponentProps,
} from "@coltorapps/builder-react";

export function InterpreterTextFieldEntity(
  props: InterpreterEntityComponentProps<typeof textFieldEntity>,
) {
  const value = useEntityValue(props.entity);

  const error = useEntityError(props.entity);

  return (
    <div>
      <input
        value={value}
        onChange={(value) => {
          props.entity.setValue(value);

          void props.entity.validate();
        }}
      />
      {error ? <p className="text-red-500">{String(error)}</p> : null}
    </div>
  );
}

Validate on change after a submission attempt

To validate an entity on value change only after a form submission attempt has occurred, you can use a ref to track submission state:

import { useRef } from "react";

import {
  InterpreterEntities,
  useEntityValueUpdated,
  useInterpreterStore,
} from "@coltorapps/builder-react";

const components = { textField: InterpreterTextFieldEntity };

export function App() {
  const interpreterStore = useInterpreterStore(formBuilder, formSchema);

  const submitAttemptedRef = useRef(false);

  useEntityValueUpdated(interpreterStore, (entity) => {
    if (submitAttemptedRef.current) {
      void interpreterStore.validateEntityValue(entity.id);
    }
  });

  async function submitForm() {
    submitAttemptedRef.current = true;

    const validationResult = await interpreterStore.validateEntitiesValues();

    if (validationResult.success) {
      // Send to server.
    }
  }

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();

        void submitForm();
      }}
    >
      <InterpreterEntities
        interpreterStore={interpreterStore}
        components={components}
      />
      <button type="submit">Submit</button>
    </form>
  );
}

Validate on blur

The library doesn’t have built-in support for blur events, as they are considered out of scope, so you’ll need to handle them manually at the interpreter entity component level.

import {
  useEntityError,
  useEntityValue,
  type InterpreterEntityComponentProps,
} from "@coltorapps/builder-react";

export function InterpreterTextFieldEntity(
  props: InterpreterEntityComponentProps<typeof textFieldEntity>,
) {
  const value = useEntityValue(props.entity);

  const error = useEntityError(props.entity);

  return (
    <div>
      <input
        value={value}
        onChange={(value) => props.entity.setValue(value)}
        onBlur={() => void props.entity.validate()}
      />
      {error ? <p className="text-red-500">{String(error)}</p> : null}
    </div>
  );
}

You can trigger validation on any event—such as on blur, on touch, or others—depending on your UX needs.

Previous
Drag & drop

Canary Branch