| title | useWorkAddressForm |
|---|---|
| order | 4 |
Creates or updates an employee's work address — selecting a company location and an effective date.
import { useWorkAddressForm, SDKFormProvider } from '@gusto/embedded-react-sdk'useWorkAddressForm accepts a single options object:
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
companyId |
string |
Yes | — | The UUID of the company. Used to fetch available locations. |
employeeId |
string |
No | — | The UUID of the employee. Optional for composed form scenarios where the ID isn't known until a prior form submits — pass it via onSubmit options instead. Required for update mode (fetches existing data). |
withEffectiveDateField |
boolean |
No | true |
Whether to include the effective date field. When false, pass effective date via onSubmit options instead. |
requiredFields |
WorkAddressField[] | { create?: WorkAddressField[], update?: WorkAddressField[] } |
No | — | Additional fields to make required beyond API defaults. A flat array applies to both modes; an object targets specific modes. |
defaultValues |
Partial<WorkAddressFormData> |
No | — | Pre-fill form values. Server data takes precedence when editing an existing work address. |
validationMode |
'onSubmit' | 'onBlur' | 'onChange' | 'onTouched' | 'all' |
No | 'onSubmit' |
When validation runs. Passed through to react-hook-form. |
shouldFocusError |
boolean |
No | true |
Auto-focus the first invalid field on submit. Set to false when using composeSubmitHandler. |
The requiredFields arrays accept these field names:
type WorkAddressField = 'locationUuid' | 'effectiveDate'Required by default on create: locationUuid
Required by default on update: locationUuid
All WorkAddressField values are available to require in either mode. For example, pass requiredFields: ['effectiveDate'] to make the effective date required on create. Note that effectiveDate requirements are ignored when withEffectiveDateField is false. On update mode, the effective date field is always hidden (the API does not accept effective date changes on existing work addresses).
// Flat array: require both fields in both modes
useWorkAddressForm({ companyId, employeeId, requiredFields: ['locationUuid', 'effectiveDate'] })The shape of defaultValues:
interface WorkAddressFormData {
locationUuid: string // UUID of a company location
effectiveDate: string // ISO date string (YYYY-MM-DD)
}The hook returns a discriminated union on isLoading.
{
isLoading: true
errorHandling: HookErrorHandling
}{
isLoading: false
data: {
workAddress: EmployeeWorkAddress | null
workAddresses: EmployeeWorkAddress[]
companyLocations: Location[]
}
status: {
isPending: boolean
mode: 'create' | 'update'
}
actions: {
onSubmit: (
callbacks?: WorkAddressSubmitCallbacks,
options?: WorkAddressSubmitOptions,
) => Promise<HookSubmitResult<EmployeeWorkAddress> | undefined>
}
errorHandling: HookErrorHandling
form: {
Fields: WorkAddressFormFields
fieldsMetadata: WorkAddressFieldsMetadata
hookFormInternals: { formMethods: UseFormReturn }
getFormSubmissionValues: () => WorkAddressFormOutputs | undefined
}
}The hook automatically detects create vs. update mode based on whether the employee has an active work address. If an active work address exists, the hook enters update mode. Otherwise, it enters create mode.
onSubmit accepts an optional callbacks object:
interface WorkAddressSubmitCallbacks {
onWorkAddressCreated?: (workAddress: EmployeeWorkAddress) => void
onWorkAddressUpdated?: (workAddress: EmployeeWorkAddress) => void
}When withEffectiveDateField is false, you can pass the effective date programmatically via the second argument to onSubmit:
interface WorkAddressSubmitOptions {
employeeId?: string // Provide at submit time for composed forms where employeeId is not in props
effectiveDate?: string // ISO date string (YYYY-MM-DD)
}await workAddress.actions.onSubmit(callbacks, { effectiveDate: '2025-06-01' })If withEffectiveDateField is true, the effective date from the form field takes precedence over the submit option.
All fields accept label (required) and description (optional). Fields with validation accept validationMessages mapping error codes to display strings.
const WorkAddressErrorCodes = {
REQUIRED: 'REQUIRED',
} as constSelect dropdown for choosing the employee's work location from the company's registered locations.
| Prop | Type | Required |
|---|---|---|
label |
string |
Yes |
description |
ReactNode |
No |
validationMessages |
{ REQUIRED: string } |
No |
getOptionLabel |
(location: Location) => string |
No |
FieldComponent |
ComponentType<SelectProps> |
No |
Options: Dynamically populated from the company's locations. By default, each option displays the location's formatted inline address (e.g., "123 Main St, San Francisco, CA 94105"). This works without any extra configuration:
<Fields.Location
label="Work address"
description="The primary location where the employee will be working."
validationMessages={{ REQUIRED: 'Work address is required' }}
/>If you want to customize how locations are displayed, pass getOptionLabel. The callback receives the full Location entity, giving you access to all address fields:
<Fields.Location
label="Work address"
description="The primary location where the employee will be working."
getOptionLabel={location =>
`${location.streetOne}, ${location.city}, ${location.state} ${location.zip}`
}
validationMessages={{ REQUIRED: 'Work address is required' }}
/>Always required (in both create and update modes).
Date picker for when the work address takes effect.
| Prop | Type | Required |
|---|---|---|
label |
string |
Yes |
description |
ReactNode |
No |
validationMessages |
{ REQUIRED: string } |
No |
FieldComponent |
ComponentType<DatePickerProps> |
No |
Conditional availability: This field is undefined in update mode or when withEffectiveDateField is false. The API does not accept effective date changes on existing work addresses, so the field is only shown when creating a new work address with withEffectiveDateField enabled.
Always check for existence before rendering:
{
Fields.EffectiveDate && (
<Fields.EffectiveDate
label="Effective date"
description="The date this work address takes effect."
validationMessages={{ REQUIRED: 'Effective date is required' }}
/>
)
}A complete example showing both fields, getOptionLabel usage, and submit handling:
import {
useWorkAddressForm,
SDKFormProvider,
type UseWorkAddressFormReady,
} from '@gusto/embedded-react-sdk'
function WorkAddressPage({ companyId, employeeId }: { companyId: string; employeeId: string }) {
const workAddress = useWorkAddressForm({ companyId, employeeId })
if (workAddress.isLoading) {
const { errors, retryQueries } = workAddress.errorHandling
if (errors.length > 0) {
return (
<div>
<p>Failed to load work address data.</p>
<ul>
{errors.map((error, i) => (
<li key={i}>{error.message}</li>
))}
</ul>
<button onClick={retryQueries}>Retry</button>
</div>
)
}
return <div>Loading...</div>
}
return <WorkAddressFormReady workAddress={workAddress} />
}
function WorkAddressFormReady({ workAddress }: { workAddress: UseWorkAddressFormReady }) {
const { Fields } = workAddress.form
const handleSubmit = async () => {
const result = await workAddress.actions.onSubmit({
onWorkAddressCreated: wa => {
console.log('Work address created:', wa.uuid)
},
onWorkAddressUpdated: wa => {
console.log('Work address updated:', wa.uuid)
},
})
if (result) {
console.log(`${result.mode}d work address:`, result.data.uuid)
}
}
return (
<SDKFormProvider formHookResult={workAddress}>
<form
onSubmit={e => {
e.preventDefault()
void handleSubmit()
}}
>
<h2>{workAddress.status.mode === 'create' ? 'Add Work Address' : 'Edit Work Address'}</h2>
{workAddress.errorHandling.errors.length > 0 && (
<div role="alert">
{workAddress.errorHandling.errors.map((error, i) => (
<p key={i}>{error.message}</p>
))}
</div>
)}
<Fields.Location
label="Work address"
description="The primary location where the employee will be working."
getOptionLabel={location =>
`${location.streetOne}, ${location.city}, ${location.state} ${location.zip}`
}
validationMessages={{ REQUIRED: 'Work address is required' }}
/>
{Fields.EffectiveDate && (
<Fields.EffectiveDate
label="Effective date"
description="The date this work address takes effect."
validationMessages={{ REQUIRED: 'Effective date is required' }}
/>
)}
<button type="submit" disabled={workAddress.status.isPending}>
{workAddress.status.mode === 'create' ? 'Save work address' : 'Save changes'}
</button>
</form>
</SDKFormProvider>
)
}The same form using prop-based field connection. No SDKFormProvider wrapper needed:
import { useWorkAddressForm, type UseWorkAddressFormReady } from '@gusto/embedded-react-sdk'
function WorkAddressPage({ companyId, employeeId }: { companyId: string; employeeId: string }) {
const workAddress = useWorkAddressForm({ companyId, employeeId })
if (workAddress.isLoading) {
return <div>Loading...</div>
}
return <WorkAddressFormReady workAddress={workAddress} />
}
function WorkAddressFormReady({ workAddress }: { workAddress: UseWorkAddressFormReady }) {
const { Fields } = workAddress.form
return (
<form
onSubmit={e => {
e.preventDefault()
void workAddress.actions.onSubmit()
}}
>
<h2>{workAddress.status.mode === 'create' ? 'Add Work Address' : 'Edit Work Address'}</h2>
{workAddress.errorHandling.errors.length > 0 && (
<div role="alert">
{workAddress.errorHandling.errors.map((error, i) => (
<p key={i}>{error.message}</p>
))}
</div>
)}
<Fields.Location
label="Work address"
formHookResult={workAddress}
description="The primary location where the employee will be working."
validationMessages={{ REQUIRED: 'Work address is required' }}
/>
{Fields.EffectiveDate && (
<Fields.EffectiveDate
label="Effective date"
formHookResult={workAddress}
description="The date this work address takes effect."
validationMessages={{ REQUIRED: 'Effective date is required' }}
/>
)}
<button type="submit" disabled={workAddress.status.isPending}>
{workAddress.status.mode === 'create' ? 'Save work address' : 'Save changes'}
</button>
</form>
)
}Both examples produce identical validation, error handling, and API behavior. The prop-based approach is particularly useful when embedding work address fields within a larger composed form — see Composing Multiple Hooks.