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
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.
| Property | Type | Description |
|---|---|---|
courtOrdered | boolean | Court-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. |
employeeId | string | UUID of the employee whose deduction is being created or edited. |
defaultValues? | Partial<DeductionFormData> | Pre-fill form values. Server data takes precedence on update. |
garnishmentId? | string | When set, loads that garnishment and updates it (PUT). When omitted, the form is in create mode (POST). |
optionalFieldsToRequire? | DeductionFormOptionalFieldsToRequire | Override fields that are optional on a given mode to be required. See DeductionFormOptionalFieldsToRequire. |
shouldFocusError? | boolean | Auto-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
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.*.
| Property | Type | Description |
|---|---|---|
actions | object | Submission 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. |
data | object | Deduction-specific data payload: the loaded garnishment for update mode, or null in create mode. |
data.deduction | Garnishment | null | The garnishment loaded for update; null in create mode. |
errorHandling | HookErrorHandling | Error state and recovery actions. |
form | object | Form bindings: pre-bound field components, per-field metadata, submission values, and react-hook-form internals. |
form.Fields | DeductionFormFields | - |
form.fieldsMetadata | DeductionFormFieldsMetadata | - |
form.getFormSubmissionValues | () => DeductionFormData | undefined | - |
form.hookFormInternals | HookFormInternals<DeductionFormData> | - |
isLoading | false | Always false in this branch; discriminates from HookLoadingResult. |
status | object | Submission state and reactive flags derived from current form input. |
status.isPending | boolean | true while a create or update mutation is in flight. |
status.isRecurring | boolean | Mirrors 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.
| Property | Type | Description |
|---|---|---|
Amount | ComponentType<AmountFieldProps> | Bound to amount. Deduction amount input. Always available. |
DeductAsPercentage | ComponentType<DeductAsPercentageFieldProps> | Bound to deductAsPercentage. Fixed-amount vs percentage radio group. Always available. |
Description | ComponentType<DescriptionFieldProps> | Bound to description. Description text input. Always available. |
Recurring | ComponentType<RecurringFieldProps> | Bound to recurring. Recurring vs one-time radio group. Always available. |
AnnualMaximum | ComponentType<AnnualMaximumFieldProps> | undefined | Bound to annualMaximum. Only available when status.isRecurring is true. |
GarnishmentType | ComponentType<GarnishmentTypeFieldProps> | undefined | Bound to garnishmentType. Only available when courtOrdered: true. |
TotalAmount | ComponentType<TotalAmountFieldProps> | undefined | Bound 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.
| Property | Type | Description |
|---|---|---|
label | string | Visible 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.
| Property | Type | Description |
|---|---|---|
label | string | Visible 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.
| Property | Type | Description |
|---|---|---|
label | string | Visible 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) => string | Maps 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.
| Property | Type | Description |
|---|---|---|
label | string | Visible 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.
| Property | Type | Description |
|---|---|---|
label | string | Visible label rendered above the field. |
placeholder | string | Placeholder 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) => string | Maps 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.
| Property | Type | Description |
|---|---|---|
label | string | Visible 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) => string | Maps 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.
| Property | Type | Description |
|---|---|---|
label | string | Visible 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
| Property | Type |
|---|---|
amount | number |
annualMaximum | number |
deductAsPercentage | boolean |
description | string |
garnishmentType | "child_support" | "federal_tax_lien" | "state_tax_lien" | "student_loan" | "creditor_garnishment" | "federal_loan" | "other_garnishment" |
recurring | boolean |
totalAmount | number |
DeductionFormErrorCode
DeductionFormErrorCode =
"REQUIRED"|"NEGATIVE_AMOUNT"
Union of validation error code strings emitted by the deduction form schema.
DeductionFormErrorCodes
constDeductionFormErrorCodes:object
Validation error codes emitted by the deduction form schema. Map these
codes to localized copy in validationMessages when composing the hook.
Type Declaration
| Name | Type |
|---|---|
NEGATIVE_AMOUNT | "NEGATIVE_AMOUNT" |
REQUIRED | "REQUIRED" |
DeductionFormFieldsMetadata
| Field | Type |
|---|---|
amount | FieldMetadata |
annualMaximum | FieldMetadata |
deductAsPercentage | FieldMetadataWithOptions<boolean> |
description | FieldMetadata |
garnishmentType | FieldMetadata |
recurring | FieldMetadataWithOptions<boolean> |
totalAmount | FieldMetadata |
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<typeofrequiredFieldsConfig>
Keys of optional deduction fields that can be promoted to required via the
hook's optionalFieldsToRequire option.