Skip to main content

useDeductionForm

useDeductionForm(input: UseDeductionFormProps): UseDeductionFormResult

Headless hook for creating or updating a non-child-support deduction.

Remarks

Both variants — post-tax custom deductions and court-ordered garnishments — share the same field set (description, frequency, deduct-as-percentage, amount, optional caps) and differ only in whether the deduction is court-ordered and carries a garnishmentType. Set courtOrdered: true to surface the garnishment-type select; set it to false for a custom post-tax deduction.

Presence or absence of garnishmentId selects the API verb: omit it to POST a new deduction, supply it to PUT updates against the existing row. For child-support garnishments, use useChildSupportGarnishmentForm instead — those require agency-keyed required attributes (case number, order number, remittance number, county) that this hook doesn't model.

Example

Example
import { useDeductionForm, SDKFormProvider } from '@gusto/embedded-react-sdk'

function CustomDeductionPage({ employeeId, garnishmentId }: { employeeId: string; garnishmentId?: string }) {
const form = useDeductionForm({ employeeId, garnishmentId, courtOrdered: false })

if (form.isLoading) return <p>Loading…</p>

const { Fields } = form.form

return (
<SDKFormProvider formHookResult={form}>
<form
onSubmit={e => {
e.preventDefault()
void form.actions.onSubmit()
}}
>
<Fields.Description label="Description" validationMessages={{ REQUIRED: 'Required' }} />
<Fields.Recurring
label="Frequency"
getOptionLabel={v => (v ? 'Recurring' : 'One-time')}
validationMessages={{ REQUIRED: 'Required' }}
/>
<Fields.Amount
label="Amount"
validationMessages={{ REQUIRED: 'Required', NEGATIVE_AMOUNT: 'Must be ≥ 0' }}
/>
{Fields.TotalAmount && (
<Fields.TotalAmount label="Total cap" validationMessages={{ NEGATIVE_AMOUNT: 'Must be ≥ 0' }} />
)}
<button type="submit">Save</button>
</form>
</SDKFormProvider>
)
}

Props

UseDeductionFormProps

Configuration options for useDeductionForm.

Remarks

Presence or absence of garnishmentId selects the API verb — see the garnishmentId field description. courtOrdered selects between the post-tax custom variant and the court-ordered garnishment variant.

PropertyTypeDescription
courtOrderedbooleanCourt-ordered deductions are stored as garnishments with courtOrdered: true and require a garnishmentType (Federal Tax Lien, Student Loan, etc.). When false, the form is for a "custom" post-tax deduction — garnishmentType is excluded from the schema and submit payload. Note: this hook does NOT handle garnishmentType: 'child_support'. Use useChildSupportGarnishmentForm for child-support agency-keyed payloads.
employeeIdstringUUID of the employee whose deduction is being created or edited.
defaultValues?Partial<DeductionFormData>Pre-fill form values. Server data takes precedence on update.
garnishmentId?stringWhen set, loads that garnishment and updates it (PUT). When omitted, the form is in create mode (POST).
optionalFieldsToRequire?DeductionFormOptionalFieldsToRequireOverride fields that are optional on a given mode to be required. See DeductionFormOptionalFieldsToRequire.
shouldFocusError?booleanAuto-focus the first invalid field on submit. Set to false when using composeSubmitHandler so submit-time focus is coordinated across multiple forms. Defaults to true.
validationMode?"onChange" | "onBlur" | "onSubmit" | "onTouched" | "all"Passed through to react-hook-form. Defaults to 'onSubmit'.

Returns

UseDeductionFormResult

A HookLoadingResult while loading, or a UseDeductionFormReady once ready.

UseDeductionFormResult

UseDeductionFormResult = HookLoadingResult | UseDeductionFormReady

Return value of useDeductionForm.

Remarks

Discriminated union: HookLoadingResult while the existing garnishment is loading (update mode only); UseDeductionFormReady once data is ready. In create mode the hook returns the ready branch immediately.


UseDeductionFormReady

Ready-state shape returned by useDeductionForm once data has loaded.

Remarks

Discriminated by isLoading: false. Extends BaseFormHookReady with the deduction-specific data, status, actions, and form.Fields shape. Static, entity-derived values live under data.*; reactive values that flip with form input live under status.*.

PropertyTypeDescription
actionsobjectSubmission action.
actions.onSubmit() => Promise<HookSubmitResult<Garnishment> | undefined>Submits the form. Returns the saved garnishment + mode on success, or undefined when validation fails or the request errored.
dataobjectDeduction-specific data payload: the loaded garnishment for update mode, or null in create mode.
data.deductionGarnishment | nullThe garnishment loaded for update; null in create mode.
errorHandlingHookErrorHandlingError state and recovery actions.
formobjectForm bindings: pre-bound field components, per-field metadata, submission values, and react-hook-form internals.
form.FieldsDeductionFormFields-
form.fieldsMetadataDeductionFormFieldsMetadata-
form.getFormSubmissionValues() => DeductionFormData | undefined-
form.hookFormInternalsHookFormInternals<DeductionFormData>-
isLoadingfalseAlways false in this branch; discriminates from HookLoadingResult.
statusobjectSubmission state and reactive flags derived from current form input.
status.isPendingbooleantrue while a create or update mutation is in flight.
status.isRecurringbooleanMirrors the watched recurring value. Cap fields (TotalAmount, AnnualMaximum) are only included on Fields when this is true — the consumer can render them unconditionally and the gating happens in the hook.
status.mode"create" | "update"Reflects whether the next submit will POST a new deduction or PUT an existing one.

Fields

DeductionFormFields

Pre-bound field components exposed on useDeductionForm().form.Fields.

Remarks

Each property is either the field component or undefined. A field is undefined when conditions for rendering it aren't met — see each member for its visibility rule. Always null-check conditional fields (e.g. {Fields.TotalAmount && <Fields.TotalAmount ... />}) before rendering.

PropertyTypeDescription
AmountComponentType<AmountFieldProps>Bound to amount. Deduction amount input. Always available.
DeductAsPercentageComponentType<DeductAsPercentageFieldProps>Bound to deductAsPercentage. Fixed-amount vs percentage radio group. Always available.
DescriptionComponentType<DescriptionFieldProps>Bound to description. Description text input. Always available.
RecurringComponentType<RecurringFieldProps>Bound to recurring. Recurring vs one-time radio group. Always available.
AnnualMaximumComponentType<AnnualMaximumFieldProps> | undefinedBound to annualMaximum. Only available when status.isRecurring is true.
GarnishmentTypeComponentType<GarnishmentTypeFieldProps> | undefinedBound to garnishmentType. Only available when courtOrdered: true.
TotalAmountComponentType<TotalAmountFieldProps> | undefinedBound to totalAmount. Only available when status.isRecurring is true.

Amount

Bound to amount. Deduction amount input. Always available.

<form.Fields.Amount
label="Amount"
validationMessages={{ REQUIRED: '…', NEGATIVE_AMOUNT: '…' }}
/>

DeductionAmountFieldProps

HookFieldProps<NumberInputHookFieldProps<DeductionFormAmountValidation>>

Props accepted by useDeductionForm's Fields.Amount component.

PropertyTypeDescription
labelstringVisible label rendered above the field.
FieldComponent?ComponentType<NumberInputProps>Replaces the default number input UI component; must accept the same props as NumberInputProps.
validationMessages?ValidationMessages<DeductionFormAmountValidation>Custom error text keyed by validation error code.

Also accepts description, format, formHookResult, max, min, placeholder from NumberInputHookFieldProps.


AnnualMaximum

Bound to annualMaximum. Only available when status.isRecurring is true.

{form.Fields.AnnualMaximum && (
<form.Fields.AnnualMaximum
label="Annual maximum"
validationMessages={{ NEGATIVE_AMOUNT: '…' }}
/>
)}

AnnualMaximumFieldProps

HookFieldProps<NumberInputHookFieldProps<DeductionFormCapValidation>>

Props accepted by useDeductionForm's Fields.AnnualMaximum component.

PropertyTypeDescription
labelstringVisible label rendered above the field.
FieldComponent?ComponentType<NumberInputProps>Replaces the default number input UI component; must accept the same props as NumberInputProps.
validationMessages?ValidationMessages<DeductionFormCapValidation>Custom error text keyed by validation error code.

Also accepts description, format, formHookResult, max, min, placeholder from NumberInputHookFieldProps.


DeductAsPercentage

Bound to deductAsPercentage. Fixed-amount vs percentage radio group. Always available.

<form.Fields.DeductAsPercentage
label="Deduct as percentage"
validationMessages={{ REQUIRED: '…' }}
/>

DeductAsPercentageFieldProps

HookFieldProps<RadioGroupHookFieldProps<DeductionFormRequiredValidation, boolean>>

Props accepted by useDeductionForm's Fields.DeductAsPercentage component.

PropertyTypeDescription
labelstringVisible label rendered above the field.
FieldComponent?ComponentType<RadioGroupProps>Replaces the default radio group UI component; must accept the same props as RadioGroupProps.
getOptionLabel?(entry: boolean) => stringMaps a raw option entry to its display label; when omitted, options use the labels provided by the hook.
validationMessages?ValidationMessages<DeductionFormRequiredValidation>Custom error text keyed by validation error code.

Also accepts description, formHookResult from RadioGroupHookFieldProps.


Description

Bound to description. Description text input. Always available.

<form.Fields.Description
label="Description"
validationMessages={{ REQUIRED: '…' }}
/>

DescriptionFieldProps

HookFieldProps<TextInputHookFieldProps<DeductionFormRequiredValidation>>

Props accepted by useDeductionForm's Fields.Description component.

PropertyTypeDescription
labelstringVisible label rendered above the field.
FieldComponent?ComponentType<TextInputProps>Replaces the default text input UI component; must accept the same props as TextInputProps.
validationMessages?ValidationMessages<DeductionFormRequiredValidation>Custom error text keyed by validation error code.

Also accepts description, formHookResult, placeholder, transform from TextInputHookFieldProps.


GarnishmentType

Bound to garnishmentType. Only available when courtOrdered: true.

{form.Fields.GarnishmentType && (
<form.Fields.GarnishmentType
label="Garnishment type"
validationMessages={{ REQUIRED: '…' }}
/>
)}

GarnishmentTypeFieldProps

HookFieldProps<SelectHookFieldProps<DeductionFormRequiredValidation, GarnishmentType>>

Props accepted by useDeductionForm's Fields.GarnishmentType component.

PropertyTypeDescription
labelstringVisible label rendered above the field.
placeholderstringPlaceholder text displayed when no option is selected. Required so empty dropdowns always communicate the action — pass an empty string only when a default value is guaranteed.
FieldComponent?ComponentType<SelectProps>Replaces the default select UI component; must accept the same props as SelectProps.
getOptionLabel?(entry: GarnishmentType) => stringMaps a raw option entry to its display label; when omitted, options use the labels provided by the hook.
validationMessages?ValidationMessages<DeductionFormRequiredValidation>Custom error text keyed by validation error code.

Also accepts description, formHookResult, portalContainer from SelectHookFieldProps.


Recurring

Bound to recurring. Recurring vs one-time radio group. Always available.

<form.Fields.Recurring
label="Recurring"
validationMessages={{ REQUIRED: '…' }}
/>

RecurringFieldProps

HookFieldProps<RadioGroupHookFieldProps<DeductionFormRequiredValidation, boolean>>

Props accepted by useDeductionForm's Fields.Recurring component.

PropertyTypeDescription
labelstringVisible label rendered above the field.
FieldComponent?ComponentType<RadioGroupProps>Replaces the default radio group UI component; must accept the same props as RadioGroupProps.
getOptionLabel?(entry: boolean) => stringMaps a raw option entry to its display label; when omitted, options use the labels provided by the hook.
validationMessages?ValidationMessages<DeductionFormRequiredValidation>Custom error text keyed by validation error code.

Also accepts description, formHookResult from RadioGroupHookFieldProps.


TotalAmount

Bound to totalAmount. Only available when status.isRecurring is true.

{form.Fields.TotalAmount && (
<form.Fields.TotalAmount
label="Total amount"
validationMessages={{ NEGATIVE_AMOUNT: '…' }}
/>
)}

TotalAmountFieldProps

HookFieldProps<NumberInputHookFieldProps<DeductionFormCapValidation>>

Props accepted by useDeductionForm's Fields.TotalAmount component.

PropertyTypeDescription
labelstringVisible label rendered above the field.
FieldComponent?ComponentType<NumberInputProps>Replaces the default number input UI component; must accept the same props as NumberInputProps.
validationMessages?ValidationMessages<DeductionFormCapValidation>Custom error text keyed by validation error code.

Also accepts description, format, formHookResult, max, min, placeholder from NumberInputHookFieldProps.

Validations

DeductionFormAmountValidation

DeductionFormAmountValidation = DeductionFormRequiredValidation | DeductionFormNegativeAmountValidation

Validation error codes emitted by the amount field of useDeductionForm.

Remarks

Use these as keys in validationMessages on Fields.Amount. See DeductionFormErrorCodes for the full description of each code.


DeductionFormCapValidation

DeductionFormCapValidation = DeductionFormNegativeAmountValidation

Validation error codes emitted by the cap fields of useDeductionForm (totalAmount, annualMaximum).

Remarks

Use these as keys in validationMessages on Fields.TotalAmount and Fields.AnnualMaximum. See DeductionFormErrorCodes for the full description of each code.


DeductionFormNegativeAmountValidation

DeductionFormNegativeAmountValidation = "NEGATIVE_AMOUNT"

The negative-amount error code produced by useDeductionForm's currency fields.

Remarks

Used as a validationMessages key on Fields.Amount, Fields.TotalAmount, and Fields.AnnualMaximum. See DeductionFormErrorCodes.


DeductionFormRequiredValidation

DeductionFormRequiredValidation = "REQUIRED"

The required-field error code produced by useDeductionForm fields that only emit REQUIRED.

Remarks

Used as the validationMessages key for the description, recurring, deduct-as-percentage, and garnishment-type fields. See DeductionFormErrorCodes.

Utility types

DeductionFormData

Shape of the values managed by the deduction form.

Properties

PropertyType
amountnumber
annualMaximumnumber
deductAsPercentageboolean
descriptionstring
garnishmentType"child_support" | "federal_tax_lien" | "state_tax_lien" | "student_loan" | "creditor_garnishment" | "federal_loan" | "other_garnishment"
recurringboolean
totalAmountnumber

DeductionFormErrorCode

DeductionFormErrorCode = "REQUIRED" | "NEGATIVE_AMOUNT"

Union of validation error code strings emitted by the deduction form schema.


DeductionFormErrorCodes

const DeductionFormErrorCodes: object

Validation error codes emitted by the deduction form schema. Map these codes to localized copy in validationMessages when composing the hook.

Type Declaration

NameType
NEGATIVE_AMOUNT"NEGATIVE_AMOUNT"
REQUIRED"REQUIRED"

DeductionFormFieldsMetadata

FieldType
amountFieldMetadata
annualMaximumFieldMetadata
deductAsPercentageFieldMetadataWithOptions<boolean>
descriptionFieldMetadata
garnishmentTypeFieldMetadata
recurringFieldMetadataWithOptions<boolean>
totalAmountFieldMetadata

Per-field metadata returned by useDeductionForm as form.fieldsMetadata.

Remarks

Carries per-field isRequired, isDisabled, label, description, and option entries derived from the schema and form state. Use these to drive UI such as disabled state or option lists when not relying on the pre-bound DeductionFormFields components.


DeductionFormOptionalFieldsToRequire

DeductionFormOptionalFieldsToRequire = OptionalFieldsToRequire<typeof requiredFieldsConfig>

Keys of optional deduction fields that can be promoted to required via the hook's optionalFieldsToRequire option.


Endpoints