Skip to content

Options

When using ReactForm, you can pass options to customize its behavior. These options allow you to define global behavior through ReactFormProvider or form-specific behavior through ReactForm props.

componentPrefix

  • Type: string
  • Default: ''

Adds a prefix to component names. Not used in React (kept for consistency with React version).

isNewItemCheck

  • Type: Function
  • Arguments: (context) => Boolean

Read more

errorAdapter

  • Type: Function
  • Arguments: (error) => Object

Read more

isArchivedItemCheck

  • Type: Function
  • Arguments: (response) => Boolean

Function that determines if an item is archived. Called after read, create, update, archive, or unarchive operations.

jsx
{
  isArchivedItemCheck: (response) => {
    return response.isArchived === true;
  };
}

schemaToFields

  • Type: Function
  • Arguments: (schema) => Array<{ name: string, label?: string, value?: any }>

Custom function to extract field definitions from your validation schema. This function is called when a schema prop is provided to <ReactForm>.

With Zod
jsx
import { z } from "zod";

const schema = z.object({
  name: z
    .string()
    .min(2, "Name must be at least 2 characters")
    .default("") // Must Pass default for validate schema properly
    .describe("Name"), // Customize Label
  email: z
    .string()
    .email("Invalid email address")
    .default("") // Must Pass default for validate schema properly
    .describe("Email"), // Customize Label
});

const schemaToFields = (schema) => {
  return Object.keys(schema.shape).map((key) => {
    const zodField = schema.shape[key];
    /* 
      - "defaultValue" & "description" key can be vary or not found 
      with different zod version (current - v4)
      - check it with console.log("zodField:", zodField);  
      - Need to map the keys accordingly
      */
    const defaultValue = zodField.def.defaultValue;
    const label = zodField.description;

    return {
      name: key,
      label: label || key.charAt(0).toUpperCase() + key.slice(1),
      value: defaultValue,
    };
  });
};

<ReactFormProvider schemaToFields={schemaToFields}>
  <ReactForm schema={schema} {...otherProps} />
</ReactFormProvider>;
With Yup
jsx
import * as yup from "yup";

const schema = yup.object({
  name: yup
    .string()
    .min(2, "Name must be at least 2 characters")
    .default("") // Must Pass default for validate schema properly
    .label("Name"), // Customize Label
  email: yup
    .string()
    .email("Invalid email address")
    .default("") // Must Pass default for validate schema properly
    .label("Email"), // Customize Label
});

const schemaToFields = (schema) => {
  return Object.keys(schema.fields).map((key) => {
    const yupField = schema.fields[key];
    /* 
      - ".spec.default" & ".spec.label" key can be vary or not found 
      with different yup version (current - 1.7.1)
      - check it with console.log("yupField:", yupField);  
      - Need to map the keys accordingly
      */
    const label = yupField.spec.label;
    const defaultValue = yupField.spec.default;
    return {
      name: key,
      label: label || key.charAt(0).toUpperCase() + key.slice(1),
      value: defaultValue,
    };
  });
};

<ReactFormProvider schemaToFields={schemaToFields}>
  <ReactForm schema={schema} {...otherProps} />
</ReactFormProvider>;

With Custom Validation Library

jsx
// Example with a custom validation library
const schemaToFields = (schema) => {
  return Object.keys(schema.properties).map((key) => {
    const prop = schema.properties[key];
    return {
      name: key,
      label: prop.label || key.charAt(0).toUpperCase() + key.slice(1),
      value: prop.default || null,
    };
  });
};

validateSchema

  • Type: Function
  • Arguments: (schema, values) => Promise<{ success: boolean, errors: Object }>

Custom validation function that accepts your schema and form values, and returns a validation result. This function is called automatically when create or update is triggered.

Return Format:

jsx
{
  success: boolean,  // true if validation passed, false if failed
  errors: {          // Object with field names as keys
    fieldName: {
      message: string,  // Required: error message
      [key: string]: any  // Optional: additional error properties
    }
  }
}
With Zod
jsx
import { z } from "zod";

const schema = z.object({
  name: z
    .string()
    .min(2, "Name must be at least 2 characters")
    .default("") // Must Pass default for validate schema properly
    .describe("Name"), // Customize Label
  email: z
    .string()
    .email("Invalid email address")
    .default("") // Must Pass default for validate schema properly
    .describe("Email"), // Customize Label
});

const validateSchema = async (schema, values) => {
  const result = schema.safeParse(values);

  if (result.success) {
    return { success: true, errors: {} };
  }

  const fieldErrors = result.error.issues.reduce((acc, issue) => {
    const field = issue.path[0];
    if (!field) return acc;
    acc[field] = { message: issue.message };
    return acc;
  }, {});

  return { success: false, errors: fieldErrors };
};

<ReactFormProvider validateSchema={validateSchema}>
  <ReactForm schema={schema} {...otherProps} />
</ReactFormProvider>;
With Yup
jsx
import * as yup from "yup";

const schema = yup.object({
  name: yup
    .string()
    .min(2, "Name must be at least 2 characters")
    .default("") // Must Pass default for validate schema properly
    .label("Name"), // Customize Label
  email: yup
    .string()
    .email("Invalid email address")
    .default("") // Must Pass default for validate schema properly
    .label("Email"), // Customize Label
});

const validateSchema = async (schema, values) => {
  try {
    await schema.validate(values, { abortEarly: false });
    return { success: true, errors: {} };
  } catch (error) {
    const fieldErrors = {};

    if (error.inner) {
      error.inner.forEach((err) => {
        if (err.path) {
          fieldErrors[err.path] = { message: err.message };
        }
      });
    }

    return { success: false, errors: fieldErrors };
  }
};

<ReactFormProvider validateSchema={validateSchema}>
  <ReactForm schema={schema} {...otherProps} />
</ReactFormProvider>;

With Custom Validation Library

jsx
// Example with a custom validation library
const validateSchema = async (schema, values) => {
  try {
    // Your validation logic here
    const result = await myCustomValidator.validate(schema, values);

    if (result.valid) {
      return { success: true, errors: {} };
    }

    // Convert your library's errors to ReactForm format
    const fieldErrors = {};
    result.errors.forEach((error) => {
      fieldErrors[error.field] = {
        message: error.message,
        // Include any additional error properties
        ...error.metadata,
      };
    });

    return { success: false, errors: fieldErrors };
  } catch (error) {
    return {
      success: false,
      errors: {
        _form: { message: "Validation failed" },
      },
    };
  }
};

Advanced: Cross-Field Validation (Zod)

jsx
import { z } from "zod";

const schema = z
  .object({
    password: z
      .string()
      .min(8, "Password must be at least 8 characters")
      .regex(/[A-Z]/, "Must contain uppercase letter")
      .regex(/[a-z]/, "Must contain lowercase letter")
      .regex(/[0-9]/, "Must contain a number"),
    confirmPassword: z.string(),
  })
  .refine((data) => data.password === data.confirmPassword, {
    message: "Passwords don't match",
    path: ["confirmPassword"], // Specify which field the error belongs to
  });

const validateSchema = async (schema, values) => {
  const result = schema.safeParse(values);

  if (result.success) {
    return { success: true, errors: {} };
  }

  const fieldErrors = result.error.issues.reduce((acc, issue) => {
    // Handle cross-field validation errors
    const field = issue.path.length > 0 ? issue.path[0] : null;
    if (!field) return acc;

    acc[field] = {
      message: issue.message,
    };
    return acc;
  }, {});

  return { success: false, errors: fieldErrors };
};

Advanced: Cross-Field Validation (Yup)

jsx
import * as yup from "yup";

const schema = yup.object({
  password: yup
    .string()
    .min(8, "Password must be at least 8 characters")
    .matches(/[A-Z]/, "Must contain uppercase letter")
    .matches(/[a-z]/, "Must contain lowercase letter")
    .matches(/[0-9]/, "Must contain a number")
    .required(),
  confirmPassword: yup
    .string()
    .oneOf([yup.ref("password")], "Passwords don't match")
    .required(),
});

const validateSchema = async (schema, values) => {
  try {
    await schema.validate(values, { abortEarly: false });
    return { success: true, errors: {} };
  } catch (error) {
    const fieldErrors = {};

    if (error.inner) {
      error.inner.forEach((err) => {
        if (err.path) {
          fieldErrors[err.path] = { message: err.message };
        }
      });
    }

    return { success: false, errors: fieldErrors };
  }
};

Complete Example

Here's a complete example using Zod with both schemaToFields and validateSchema:

jsx
import {
  ReactFormProvider,
  ReactForm,
  ReactFormField,
} from "@7span/react-form";
import { z } from "zod";

// Define schema
const schema = z.object({
  name: z
    .string()
    .min(2, "Name must be at least 2 characters")
    .default("") // Must need to pass for default value
    .describe("Name"), // For customizing name
  email: z
    .string()
    .email("Invalid email address")
    .default("")
    .describe("Email"),
  age: z.number().min(18, "Must be 18 or older").default(0).describe("Age"),
});

// Schema to fields converter
const schemaToFields = (schema) => {
  return Object.keys(schema.shape).map((key) => {
    const zodField = schema.shape[key];
    /* 
      - "defaultValue" & "description" key can be vary or not found 
      with different zod version (current - v4)
      - check it with console.log("zodField:", zodField);  
      - Need to map the keys accordingly
      */
    const defaultValue = zodField.def.defaultValue;
    const label = zodField.description;

    return {
      name: key,
      label: label || key.charAt(0).toUpperCase() + key.slice(1),
      value: defaultValue,
    };
  });
};

// Validation function
const validateSchema = async (schema, values) => {
  const result = schema.safeParse(values);

  if (result.success) {
    return { success: true, errors: {} };
  }

  const fieldErrors = result.error.issues.reduce((acc, issue) => {
    const field = issue.path[0];
    if (!field) return acc;
    acc[field] = { message: issue.message };
    return acc;
  }, {});

  return { success: false, errors: fieldErrors };
};

function App() {
  return (
    <ReactFormProvider
      schemaToFields={schemaToFields}
      validateSchema={validateSchema}
    >
      <ReactForm schema={schema} create={create} update={update}>
        {({ context }) => (
          <>
            <ReactFormField name="name">
              {({ field, label, error }) => (
                <>
                  <label>{label}</label>
                  <input {...field} />
                  {error && <span>{error.message}</span>}
                </>
              )}
            </ReactFormField>
            {/* More fields... */}
          </>
        )}
      </ReactForm>
    </ReactFormProvider>
  );
}

Version 1.0.0