formik-patterns

import { useFormik } from 'formik'; import * as yup from 'yup';

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "formik-patterns" with this command: npx skills add chriswiles/claude-code-showcase/chriswiles-claude-code-showcase-formik-patterns

Formik Patterns

Basic Form Setup

import { useFormik } from 'formik'; import * as yup from 'yup';

const validationSchema = yup.object({ email: yup.string().email('Invalid email').required('Email is required'), password: yup.string().min(8, 'Min 8 characters').required('Password is required'), });

const LoginForm = () => { const formik = useFormik({ initialValues: { email: '', password: '', }, validationSchema, onSubmit: async (values) => { await loginMutation({ variables: { input: values } }); }, });

return ( <VStack gap="$4"> <Input label="Email" value={formik.values.email} onChangeText={formik.handleChange('email')} onBlur={formik.handleBlur('email')} error={formik.touched.email ? formik.errors.email : undefined} keyboardType="email-address" autoCapitalize="none" />

  &#x3C;Input
    label="Password"
    value={formik.values.password}
    onChangeText={formik.handleChange('password')}
    onBlur={formik.handleBlur('password')}
    error={formik.touched.password ? formik.errors.password : undefined}
    secureTextEntry
  />

  &#x3C;Button
    onPress={formik.handleSubmit}
    isDisabled={!formik.isValid || formik.isSubmitting}
    isLoading={formik.isSubmitting}
  >
    Login
  &#x3C;/Button>
&#x3C;/VStack>

); };

Validation Schemas

Common Patterns

import * as yup from 'yup';

// Email email: yup.string() .email('Invalid email address') .required('Email is required')

// Password with requirements password: yup.string() .min(8, 'Must be at least 8 characters') .matches(/[a-z]/, 'Must contain lowercase letter') .matches(/[A-Z]/, 'Must contain uppercase letter') .matches(/[0-9]/, 'Must contain number') .required('Password is required')

// Confirm password confirmPassword: yup.string() .oneOf([yup.ref('password')], 'Passwords must match') .required('Please confirm password')

// Phone number phone: yup.string() .matches(/^+?[1-9]\d{1,14}$/, 'Invalid phone number') .required('Phone is required')

// Optional field with validation when present website: yup.string() .url('Must be a valid URL') .nullable()

// Number with range quantity: yup.number() .min(1, 'Minimum 1') .max(100, 'Maximum 100') .required('Quantity required')

// Array with minimum items tags: yup.array() .of(yup.string()) .min(1, 'Select at least one tag')

Conditional Validation

const schema = yup.object({ hasCompany: yup.boolean(), companyName: yup.string().when('hasCompany', { is: true, then: (schema) => schema.required('Company name required'), otherwise: (schema) => schema.nullable(), }), });

Form Field Helpers

Input Helper

const getFieldProps = (name: keyof typeof formik.values) => ({ value: formik.values[name], onChangeText: formik.handleChange(name), onBlur: formik.handleBlur(name), error: formik.touched[name] ? formik.errors[name] : undefined, });

// Usage <Input label="Email" {...getFieldProps('email')} />

Select/Picker Helper

<Select label="Country" value={formik.values.country} onValueChange={(value) => formik.setFieldValue('country', value)} error={formik.touched.country ? formik.errors.country : undefined} options={countryOptions} />

Form Submission with GraphQL

const CreateItemForm = () => { const [createItem] = useCreateItemMutation({ onCompleted: () => { toast.success({ title: 'Item created' }); navigation.goBack(); }, onError: (error) => { console.error('createItem failed:', error); toast.error({ title: 'Failed to create item' }); }, });

const formik = useFormik({ initialValues: { name: '', description: '' }, validationSchema, onSubmit: async (values, { setSubmitting }) => { try { await createItem({ variables: { input: values } }); } finally { setSubmitting(false); } }, });

return ( <VStack gap="$4"> {/* Form fields */} <Button onPress={formik.handleSubmit} isDisabled={!formik.isValid || formik.isSubmitting} isLoading={formik.isSubmitting} > Create </Button> </VStack> ); };

Edit Form with Initial Values

const EditItemForm = ({ item }: { item: Item }) => { const [updateItem] = useUpdateItemMutation({ onCompleted: () => toast.success({ title: 'Saved' }), onError: (error) => { console.error('updateItem failed:', error); toast.error({ title: 'Save failed' }); }, });

const formik = useFormik({ initialValues: { name: item.name, description: item.description ?? '', }, enableReinitialize: true, // Update when item prop changes validationSchema, onSubmit: async (values) => { await updateItem({ variables: { id: item.id, input: values }, }); }, });

// Track if form has changes const hasChanges = formik.dirty;

return ( <VStack gap="$4"> {/* Form fields */} <Button onPress={formik.handleSubmit} isDisabled={!hasChanges || !formik.isValid || formik.isSubmitting} isLoading={formik.isSubmitting} > Save Changes </Button> </VStack> ); };

Form State Helpers

const { values, // Current form values errors, // Validation errors touched, // Fields that have been touched isValid, // Form passes validation isSubmitting, // Submit in progress dirty, // Values differ from initial handleSubmit, // Submit handler handleChange, // Change handler handleBlur, // Blur handler setFieldValue, // Set single field setFieldTouched, // Mark field touched resetForm, // Reset to initial values setSubmitting, // Control submitting state } = formik;

Multi-Step Forms

const MultiStepForm = () => { const [step, setStep] = useState(0);

const formik = useFormik({ initialValues: { // Step 1 name: '', email: '', // Step 2 address: '', city: '', // Step 3 cardNumber: '', }, validationSchema: stepSchemas[step], onSubmit: async (values) => { if (step < steps.length - 1) { setStep(step + 1); } else { await submitOrder(values); } }, });

return ( <VStack> {step === 0 && <PersonalInfoStep formik={formik} />} {step === 1 && <AddressStep formik={formik} />} {step === 2 && <PaymentStep formik={formik} />}

  &#x3C;HStack gap="$4">
    {step > 0 &#x26;&#x26; (
      &#x3C;Button variant="outline" onPress={() => setStep(step - 1)}>
        Back
      &#x3C;/Button>
    )}
    &#x3C;Button
      onPress={formik.handleSubmit}
      isDisabled={!formik.isValid}
      isLoading={formik.isSubmitting}
    >
      {step &#x3C; steps.length - 1 ? 'Next' : 'Submit'}
    &#x3C;/Button>
  &#x3C;/HStack>
&#x3C;/VStack>

); };

Anti-Patterns

// WRONG - Not showing validation errors <Input value={formik.values.email} onChangeText={formik.handleChange('email')} />

// CORRECT - Show errors when touched <Input value={formik.values.email} onChangeText={formik.handleChange('email')} onBlur={formik.handleBlur('email')} error={formik.touched.email ? formik.errors.email : undefined} />

// WRONG - Submit button always enabled <Button onPress={formik.handleSubmit}>Submit</Button>

// CORRECT - Disabled when invalid or submitting <Button onPress={formik.handleSubmit} isDisabled={!formik.isValid || formik.isSubmitting} isLoading={formik.isSubmitting}

Submit </Button>

// WRONG - No error handling on mutation onSubmit: async (values) => { await createItem({ variables: { input: values } }); }

// CORRECT - Handle errors onSubmit: async (values, { setSubmitting }) => { try { await createItem({ variables: { input: values } }); } catch (error) { toast.error({ title: 'Failed to save' }); } finally { setSubmitting(false); } }

Integration with Other Skills

  • graphql-schema: Mutation submission patterns

  • react-ui-patterns: Loading/error states

  • testing-patterns: Test form validation and submission

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

core-components

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

testing-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

react-ui-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

systematic-debugging

No summary provided by upstream source.

Repository SourceNeeds Review