Skip to content

Custom Actions & Progress

Replace the built-in form buttons and step progress bar with your own components. Useful when your project has a design system and the default unstyled HTML doesn't fit.

Custom actions component

Pass your component via the actionsComponent (Vue) or ActionsComponent (React) prop:

vue
<script setup lang="ts">
import { FormRenderer } from '@formhaus/vue'
import MyFormActions from './MyFormActions.vue'
</script>

<template>
  <FormRenderer
    :definition="definition"
    :actions-component="MyFormActions"
    @submit="onSubmit"
  />
</template>
tsx
import { FormRenderer } from '@formhaus/react'
import { MyFormActions } from './MyFormActions'

<FormRenderer
  definition={definition}
  ActionsComponent={MyFormActions}
  onSubmit={handleSubmit}
/>

Your component receives pre-computed convenience props so you don't need to re-derive button labels or visibility:

PropTypeDescription
primaryLabelstring?Resolved label for the primary button ("Continue" or submit label)
showBackboolean?Whether the back button should be shown
backLabelstring?Resolved label for the back button
loadingboolean?Whether the form is submitting
cancelActionFormAction?Cancel button config

In React, call onPrimary for the primary action and onPrev for back. In Vue, emit primary and prev.

Vue example

vue
<script setup lang="ts">
import type { FormActionsProps } from '@formhaus/vue'

defineProps<FormActionsProps>()
const emit = defineEmits<{
  (e: 'primary'): void
  (e: 'prev'): void
  (e: 'cancel'): void
}>()
</script>

<template>
  <div class="my-actions">
    <button v-if="showBack" @click="emit('prev')">{{ backLabel }}</button>
    <button @click="emit('primary')" :disabled="loading">{{ primaryLabel }}</button>
  </div>
</template>

React example

tsx
import type { FormActionsProps } from '@formhaus/react'

export function MyFormActions({
  primaryLabel, showBack, backLabel, loading,
  onPrimary, onPrev,
}: FormActionsProps) {
  return (
    <div className="my-actions">
      {showBack && <button onClick={onPrev}>{backLabel}</button>}
      <button disabled={loading} onClick={onPrimary}>{primaryLabel}</button>
    </div>
  )
}

Raw props

For full control, the raw props are still available:

PropTypeDescription
submitActionFormAction?Label and variant for the submit button
backActionFormAction | false?Back button config, or false to hide
cancelActionFormAction?Cancel button config
isFirstStepbooleanWhether this is the first step
isLastStepbooleanWhether this is the last step
isMultiStepbooleanWhether the form has multiple steps
loadingboolean?Whether the form is submitting

In Vue, you can also emit submit, next, prev, cancel events directly. In React, call onSubmit, onNext, onPrev, onCancel callbacks.

Custom progress component

Pass your component via the progressComponent (Vue) or ProgressComponent (React) prop:

vue
<template>
  <FormRenderer
    :definition="definition"
    :progress-component="MyStepProgress"
    @submit="onSubmit"
  />
</template>
tsx
<FormRenderer
  definition={definition}
  ProgressComponent={MyStepProgress}
  onSubmit={handleSubmit}
/>

Your component receives these props:

PropTypeDescription
currentnumberCurrent step number (1-based)
totalnumberTotal visible steps
stepTitlestring?Title of the current step
stepDescriptionstring?Description of the current step

The progress component only renders for multi-step forms.

Combining both

Use all custom component props together:

vue
<template>
  <FormRenderer
    :definition="definition"
    :components="{ phone: CustomPhoneInput }"
    :actions-component="MyFormActions"
    :progress-component="MyStepProgress"
    @submit="onSubmit"
  />
</template>
tsx
<FormRenderer
  definition={definition}
  components={{ phone: CustomPhoneInput }}
  ActionsComponent={MyFormActions}
  ProgressComponent={MyStepProgress}
  onSubmit={handleSubmit}
/>

If you don't pass these props, the built-in components render as before. No breaking changes.

Exported types

Import the prop interfaces to type your custom components:

ts
// Vue
import type { FormActionsProps, FormStepProgressProps } from '@formhaus/vue'

// React
import type { FormActionsProps, FormStepProgressProps } from '@formhaus/react'

Analytics events

Track form interactions via onAnalyticsEvent (React) or @analyticsEvent (Vue):

tsx
<FormRenderer
  definition={definition}
  onAnalyticsEvent={(event) => analytics.track(event.type, event)}
  onSubmit={handleSubmit}
/>
vue
<FormRenderer
  :definition="definition"
  @analytics-event="(event) => analytics.track(event.type, event)"
  @submit="onSubmit"
/>

Events emitted:

EventWhenPayload
field_focusedUser focuses a fieldfieldKey
field_blurredUser leaves a fieldfieldKey, hasValue
field_errorValidation failsfieldKey, error
step_completedUser advances past a stepstepId
step_viewedA step becomes activestepId, stepIndex
form_submittedForm submits successfullyfieldCount

All events are optional. If you don't pass a handler, nothing fires.

Next steps