CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-formik

Build forms in React, without the tears

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

form-state-management.mddocs/

Form State Management

Hook-based form state management for functional components, providing complete form control without component wrappers.

Capabilities

useFormik Hook

Primary hook for managing form state, validation, and submission in functional components.

/**
 * Hook for form state management without component wrapper
 * @param config - Formik configuration object
 * @returns Complete formik props object with state and handlers
 */
function useFormik<Values extends FormikValues = FormikValues>(
  config: FormikConfig<Values>
): FormikProps<Values>;

interface FormikConfig<Values> {
  /** Initial values of the form */
  initialValues: Values;
  /** Form submission handler */
  onSubmit: (values: Values, formikHelpers: FormikHelpers<Values>) => void | Promise<any>;
  /** Yup schema or validation function */
  validationSchema?: any | (() => any);
  /** Custom validation function */
  validate?: (values: Values) => void | object | Promise<FormikErrors<Values>>;
  /** Validate on input change (default: true) */
  validateOnChange?: boolean;
  /** Validate on input blur (default: true) */
  validateOnBlur?: boolean;
  /** Validate on component mount (default: false) */
  validateOnMount?: boolean;
  /** Whether initial form values are valid */
  isInitialValid?: boolean | ((props: any) => boolean);
  /** Reset form when initialValues change */
  enableReinitialize?: boolean;
  /** Initial status value */
  initialStatus?: any;
  /** Initial errors object */
  initialErrors?: FormikErrors<Values>;
  /** Initial touched object */
  initialTouched?: FormikTouched<Values>;
  /** Reset handler */
  onReset?: (values: Values, formikHelpers: FormikHelpers<Values>) => void;
}

interface FormikProps<Values> {
  /** Current form values */
  values: Values;
  /** Current form errors */
  errors: FormikErrors<Values>;
  /** Fields that have been visited */
  touched: FormikTouched<Values>;
  /** Whether form is currently submitting */
  isSubmitting: boolean;
  /** Whether form is currently validating */
  isValidating: boolean;
  /** Top-level status object */
  status?: any;
  /** Number of submit attempts */
  submitCount: number;
  /** Whether form has been modified */
  dirty: boolean;
  /** Whether form is valid */
  isValid: boolean;
  /** Initial form values */
  initialValues: Values;
  /** Initial form errors */
  initialErrors: FormikErrors<Values>;
  /** Initial touched state */
  initialTouched: FormikTouched<Values>;
  /** Initial status */
  initialStatus?: any;
  /** Form submit handler */
  handleSubmit: (e?: React.FormEvent<HTMLFormElement>) => void;
  /** Form reset handler */
  handleReset: (e?: React.SyntheticEvent<any>) => void;
  /** Input blur handler */
  handleBlur: (e: React.FocusEvent<any>) => void;
  /** Input change handler */
  handleChange: (e: React.ChangeEvent<any>) => void;
  /** Get field props */
  getFieldProps: (props: string | FieldConfig) => FieldInputProps<any>;
  /** Get field meta data */
  getFieldMeta: (name: string) => FieldMetaProps<any>;
  /** Get field helpers */
  getFieldHelpers: (name: string) => FieldHelperProps<any>;
  /** Set field value */
  setFieldValue: (field: string, value: any, shouldValidate?: boolean) => Promise<void | FormikErrors<Values>>;
  /** Set field error */
  setFieldError: (field: string, message: string | undefined) => void;
  /** Set field touched state */
  setFieldTouched: (field: string, isTouched?: boolean, shouldValidate?: boolean) => Promise<void | FormikErrors<Values>>;
  /** Set form status */
  setStatus: (status?: any) => void;
  /** Set form errors */
  setErrors: (errors: FormikErrors<Values>) => void;
  /** Set form submitting state */
  setSubmitting: (isSubmitting: boolean) => void;
  /** Set form touched state */
  setTouched: (touched: FormikTouched<Values>, shouldValidate?: boolean) => Promise<void | FormikErrors<Values>>;
  /** Set form values */
  setValues: (values: React.SetStateAction<Values>, shouldValidate?: boolean) => Promise<void | FormikErrors<Values>>;
  /** Validate entire form */
  validateForm: (values?: any) => Promise<FormikErrors<Values>>;
  /** Validate single field */
  validateField: (field: string) => Promise<void> | Promise<string | undefined>;
  /** Reset form to initial state */
  resetForm: (nextState?: Partial<FormikState<Values>>) => void;
  /** Submit form programmatically */
  submitForm: () => Promise<void>;
  /** Set entire Formik state */
  setFormikState: (f: FormikState<Values> | ((prevState: FormikState<Values>) => FormikState<Values>), cb?: () => void) => void;
  /** Register field with Formik */
  registerField: (name: string, fns: { validate?: FieldValidator }) => void;
  /** Unregister field from Formik */
  unregisterField: (name: string) => void;
}

Usage Examples:

import { useFormik } from "formik";
import * as Yup from "yup";

// Basic usage
const SignupForm = () => {
  const formik = useFormik({
    initialValues: {
      firstName: '',
      lastName: '',
      email: '',
    },
    validationSchema: Yup.object({
      firstName: Yup.string()
        .max(15, 'Must be 15 characters or less')
        .required('Required'),
      lastName: Yup.string()
        .max(20, 'Must be 20 characters or less')
        .required('Required'),
      email: Yup.string().email('Invalid email address').required('Required'),
    }),
    onSubmit: values => {
      alert(JSON.stringify(values, null, 2));
    },
  });

  return (
    <form onSubmit={formik.handleSubmit}>
      <label htmlFor="firstName">First Name</label>
      <input
        id="firstName"
        name="firstName"
        type="text"
        onChange={formik.handleChange}
        onBlur={formik.handleBlur}
        value={formik.values.firstName}
      />
      {formik.touched.firstName && formik.errors.firstName ? (
        <div>{formik.errors.firstName}</div>
      ) : null}

      <label htmlFor="lastName">Last Name</label>
      <input
        id="lastName"
        name="lastName"
        type="text"
        onChange={formik.handleChange}
        onBlur={formik.handleBlur}
        value={formik.values.lastName}
      />
      {formik.touched.lastName && formik.errors.lastName ? (
        <div>{formik.errors.lastName}</div>
      ) : null}

      <label htmlFor="email">Email Address</label>
      <input
        id="email"
        name="email"
        type="email"
        onChange={formik.handleChange}
        onBlur={formik.handleBlur}
        value={formik.values.email}
      />
      {formik.touched.email && formik.errors.email ? (
        <div>{formik.errors.email}</div>
      ) : null}

      <button type="submit">Submit</button>
    </form>
  );
};

// Using getFieldProps for cleaner code
const LoginForm = () => {
  const formik = useFormik({
    initialValues: { username: '', password: '' },
    onSubmit: values => console.log(values),
  });

  return (
    <form onSubmit={formik.handleSubmit}>
      <input
        placeholder="Username"
        {...formik.getFieldProps('username')}
      />
      
      <input
        type="password"
        placeholder="Password"
        {...formik.getFieldProps('password')}
      />
      
      <button type="submit" disabled={formik.isSubmitting}>
        Submit
      </button>
    </form>
  );
};

// Async validation and submission
const AsyncForm = () => {
  const formik = useFormik({
    initialValues: { email: '' },
    validate: async (values) => {
      const errors = {};
      
      if (!values.email) {
        errors.email = 'Required';
      } else {
        // Simulate async validation
        const isAvailable = await checkEmailAvailability(values.email);
        if (!isAvailable) {
          errors.email = 'Email already taken';
        }
      }
      
      return errors;
    },
    onSubmit: async (values, { setSubmitting, setStatus }) => {
      try {
        await submitForm(values);
        setStatus({ type: 'success', message: 'Form submitted successfully!' });
      } catch (error) {
        setStatus({ type: 'error', message: 'Submission failed' });
      } finally {
        setSubmitting(false);
      }
    },
  });

  return (
    <form onSubmit={formik.handleSubmit}>
      <input {...formik.getFieldProps('email')} />
      {formik.errors.email && <div>{formik.errors.email}</div>}
      
      {formik.status && (
        <div className={formik.status.type}>
          {formik.status.message}
        </div>
      )}
      
      <button type="submit" disabled={formik.isSubmitting || formik.isValidating}>
        {formik.isSubmitting ? 'Submitting...' : 'Submit'}
      </button>
    </form>
  );
};

Validation Utilities

Functions for integrating with external validation libraries and handling validation errors.

/**
 * Convert Yup validation errors to Formik error format
 * @param yupError - Yup validation error object
 * @returns Formik-compatible errors object
 */
function yupToFormErrors<Values>(yupError: any): FormikErrors<Values>;

/**
 * Validate values against Yup schema
 * @param values - Values to validate
 * @param schema - Yup validation schema
 * @param sync - Whether to run synchronously
 * @param context - Validation context
 * @returns Promise resolving to validation errors
 */
function validateYupSchema<T extends FormikValues>(
  values: T,
  schema: any,
  sync?: boolean,
  context?: any
): Promise<Partial<T>>;

/**
 * Prepare form data for validation by handling nested objects and arrays
 * @param values - Form values to prepare
 * @returns Prepared values for validation
 */
function prepareDataForValidation<T extends FormikValues>(values: T): any;

Usage Examples:

import { useFormik, yupToFormErrors, validateYupSchema } from "formik";
import * as Yup from "yup";

// Custom validation using Yup utilities
const FormWithCustomValidation = () => {
  const schema = Yup.object({
    name: Yup.string().required('Name is required'),
    age: Yup.number().min(18, 'Must be 18 or older').required('Age is required'),
  });

  const formik = useFormik({
    initialValues: { name: '', age: '' },
    validate: async (values) => {
      try {
        await validateYupSchema(values, schema);
        return {};
      } catch (error) {
        return yupToFormErrors(error);
      }
    },
    onSubmit: values => console.log(values),
  });

  return (
    <form onSubmit={formik.handleSubmit}>
      <input {...formik.getFieldProps('name')} placeholder="Name" />
      {formik.errors.name && <div>{formik.errors.name}</div>}
      
      <input {...formik.getFieldProps('age')} type="number" placeholder="Age" />
      {formik.errors.age && <div>{formik.errors.age}</div>}
      
      <button type="submit">Submit</button>
    </form>
  );
};

// Manual error handling
const ManualValidationForm = () => {
  const formik = useFormik({
    initialValues: { username: '' },
    onSubmit: async (values, { setFieldError, setSubmitting }) => {
      try {
        await submitUser(values);
      } catch (error) {
        if (error.field === 'username') {
          setFieldError('username', error.message);
        }
      } finally {
        setSubmitting(false);
      }
    },
  });

  const handleCheckAvailability = async () => {
    if (!formik.values.username) return;
    
    try {
      const available = await checkUsernameAvailability(formik.values.username);
      if (!available) {
        formik.setFieldError('username', 'Username not available');
      } else {
        formik.setFieldError('username', undefined);
      }
    } catch (error) {
      formik.setFieldError('username', 'Error checking availability');
    }
  };

  return (
    <form onSubmit={formik.handleSubmit}>
      <input {...formik.getFieldProps('username')} placeholder="Username" />
      <button type="button" onClick={handleCheckAvailability}>
        Check Availability
      </button>
      {formik.errors.username && <div>{formik.errors.username}</div>}
      
      <button type="submit" disabled={formik.isSubmitting}>
        Submit
      </button>
    </form>
  );
};

Form State Types

Core TypeScript interfaces for form state management.

interface FormikState<Values> {
  /** Current form values */
  values: Values;
  /** Current form errors */
  errors: FormikErrors<Values>;
  /** Fields that have been visited */
  touched: FormikTouched<Values>;
  /** Whether form is currently submitting */
  isSubmitting: boolean;
  /** Whether form is currently validating */
  isValidating: boolean;
  /** Top-level status object */
  status?: any;
  /** Number of submit attempts */
  submitCount: number;
}

interface FormikComputedProps<Values> {
  /** Whether form has been modified from initial values */
  readonly dirty: boolean;
  /** Whether form passes validation */
  readonly isValid: boolean;
  /** Initial form values */
  readonly initialValues: Values;
  /** Initial form errors */
  readonly initialErrors: FormikErrors<Values>;
  /** Initial touched state */
  readonly initialTouched: FormikTouched<Values>;
  /** Initial status value */
  readonly initialStatus?: any;
}

interface FormikHelpers<Values> {
  /** Set top-level status */
  setStatus: (status?: any) => void;
  /** Set form errors object */
  setErrors: (errors: FormikErrors<Values>) => void;
  /** Set form submitting state */
  setSubmitting: (isSubmitting: boolean) => void;
  /** Set form touched state */
  setTouched: (touched: FormikTouched<Values>, shouldValidate?: boolean) => Promise<void | FormikErrors<Values>>;
  /** Set entire form values */
  setValues: (values: React.SetStateAction<Values>, shouldValidate?: boolean) => Promise<void | FormikErrors<Values>>;
  /** Set individual field value */
  setFieldValue: (field: string, value: any, shouldValidate?: boolean) => Promise<void | FormikErrors<Values>>;
  /** Set individual field error */
  setFieldError: (field: string, message: string | undefined) => void;
  /** Set individual field touched state */
  setFieldTouched: (field: string, isTouched?: boolean, shouldValidate?: boolean) => Promise<void | FormikErrors<Values>>;
  /** Validate entire form */
  validateForm: (values?: any) => Promise<FormikErrors<Values>>;
  /** Validate single field */
  validateField: (field: string) => Promise<void> | Promise<string | undefined>;
  /** Reset form to initial or provided state */
  resetForm: (nextState?: Partial<FormikState<Values>>) => void;
  /** Submit form programmatically */
  submitForm: () => Promise<void>;
  /** Set entire Formik state */
  setFormikState: (f: FormikState<Values> | ((prevState: FormikState<Values>) => FormikState<Values>), cb?: () => void) => void;
}

interface FormikHandlers {
  /** Form submit event handler */
  handleSubmit: (e?: React.FormEvent<HTMLFormElement>) => void;
  /** Form reset event handler */
  handleReset: (e?: React.SyntheticEvent<any>) => void;
  /** Input blur event handler */
  handleBlur: (e: React.FocusEvent<any>) => void;
  /** Input change event handler */
  handleChange: (e: React.ChangeEvent<any>) => void;
  /** Get field input props */
  getFieldProps: (props: string | FieldConfig) => FieldInputProps<any>;
  /** Get field metadata */
  getFieldMeta: (name: string) => FieldMetaProps<any>;
  /** Get field helper functions */
  getFieldHelpers: (name: string) => FieldHelperProps<any>;
}

Utility Functions

Type checking and object manipulation utilities for advanced form scenarios and custom integrations.

/**
 * Check if value is a function
 * @param obj - Value to check
 * @returns True if value is a function
 */
function isFunction(obj: any): obj is Function;

/**
 * Check if value is an object
 * @param obj - Value to check
 * @returns True if value is an object
 */
function isObject(obj: any): obj is Object;

/**
 * Check if value is a string
 * @param obj - Value to check
 * @returns True if value is a string
 */
function isString(obj: any): obj is string;

/**
 * Check if value is an integer
 * @param obj - Value to check
 * @returns True if value is an integer
 */
function isInteger(obj: any): boolean;

/**
 * Check if value is NaN
 * @param obj - Value to check
 * @returns True if value is NaN
 */
function isNaN(obj: any): boolean;

/**
 * Check if value is an empty array
 * @param value - Value to check
 * @returns True if value is an empty array
 */
function isEmptyArray(value?: any): boolean;

/**
 * Check if React children are empty
 * @param children - React children to check
 * @returns True if children are empty
 */
function isEmptyChildren(children: any): boolean;

/**
 * Check if value is Promise-like
 * @param value - Value to check
 * @returns True if value has then method
 */
function isPromise(value: any): value is PromiseLike<any>;

/**
 * Check if value is a React input event
 * @param value - Value to check
 * @returns True if value is a React SyntheticEvent
 */
function isInputEvent(value: any): value is React.SyntheticEvent<any>;

/**
 * Get the currently active DOM element
 * @param doc - Document object (defaults to global document)
 * @returns Currently active element or null
 */
function getActiveElement(doc?: Document): Element | null;

/**
 * Get nested object value by path
 * @param obj - Object to traverse
 * @param key - Path string or array of keys
 * @param def - Default value if path not found
 * @returns Value at path or default value
 */
function getIn(obj: any, key: string | string[], def?: any): any;

/**
 * Set nested object value by path
 * @param obj - Object to modify
 * @param path - Path string to set value at
 * @param value - Value to set
 * @returns Modified object
 */
function setIn(obj: any, path: string, value: any): any;

/**
 * Set values in nested object structure
 * @param object - Object to modify
 * @param value - Value to set throughout structure
 * @returns Modified object with values set
 */
function setNestedObjectValues<T>(object: any, value: any): T;

Usage Examples:

import { getIn, setIn, isFunction, isPromise } from "formik";

// Object path manipulation
const user = {
  profile: {
    contact: {
      email: 'user@example.com'
    }
  }
};

// Get nested value
const email = getIn(user, 'profile.contact.email'); // 'user@example.com'
const phone = getIn(user, 'profile.contact.phone', 'No phone'); // 'No phone'

// Set nested value
const updatedUser = setIn(user, 'profile.contact.phone', '555-1234');

// Type checking
const validate = (value) => {
  if (!isString(value)) {
    return 'Value must be a string';
  }
  
  if (isFunction(value)) {
    return 'Functions are not allowed';
  }
  
  // Handle async validation
  if (isPromise(value)) {
    return value.then(result => validate(result));
  }
  
  return undefined;
};

// Custom field component using utilities
const AdvancedField = ({ name, ...props }) => {
  const [field, meta, helpers] = useField(name);
  
  const handleChange = (e) => {
    if (isInputEvent(e)) {
      field.onChange(e);
    } else {
      // Handle programmatic value changes
      helpers.setValue(e);
    }
  };
  
  return (
    <input 
      {...field} 
      {...props}
      onChange={handleChange}
    />
  );
};

docs

context-integration.md

core-components.md

error-handling.md

field-arrays.md

form-state-management.md

index.md

tile.json