@letar/forms

Form Analytics

Built-in field-level analytics — track drop-offs, time per field, and completion rates

Overview

67% of forms are abandoned before completion. The password field alone has a 10.5% drop-off rate. useFormAnalytics() gives you visibility into exactly where users struggle.

import { AnalyticsPanel, createUmamiAdapter, useFormAnalytics } from '@letar/forms'

function ContactForm() {
  const analytics = useFormAnalytics({
    formId: 'contact-form',
    adapters: [createUmamiAdapter()],
  })

  return (
    <Form schema={ContactSchema} onSubmit={save}>
      <Form.Field.String name="name" />
      <Form.Field.String name="email" />
      <Form.Field.Textarea name="message" />
      <Form.Button.Submit>Send</Form.Button.Submit>

      {/* Dev-only panel */}
      {process.env.NODE_ENV === 'development' && (
        <AnalyticsPanel analytics={analytics} />
      )}
    </Form>
  )
}

What's Tracked

MetricDescription
Focus countHow many times each field received focus
Time per fieldTotal milliseconds spent on each field
Error countValidation errors per field
CorrectionsHow many times user returned to fix a field
Completion ratePercentage of fields filled (0-100%)
AbandonLast field, filled count, total time on beforeunload
CompleteTotal time and per-field breakdown on submit

Adapters

Umami

import { createUmamiAdapter } from '@letar/forms/analytics'
const adapter = createUmamiAdapter()

Yandex Metrika

import { createYandexMetrikaAdapter } from '@letar/forms/analytics'
const adapter = createYandexMetrikaAdapter(12345) // counter ID

Sends goals: form_{formId}_field_focus, form_{formId}_abandon, form_{formId}_complete.

Google Analytics 4

import { createGtagAdapter } from '@letar/forms/analytics'
const adapter = createGtagAdapter()

PostHog

import { createPostHogAdapter } from '@letar/forms/analytics'
const adapter = createPostHogAdapter()

Custom Adapter

const myAdapter: AnalyticsAdapter = {
  name: 'my-analytics',
  track(event, formId) {
    fetch('/api/analytics', {
      method: 'POST',
      body: JSON.stringify({ event, formId }),
    })
  },
}

API: useFormAnalytics(config?)

const analytics = useFormAnalytics({
  enabled: true,
  formId: 'contact-form',
  adapters: [createUmamiAdapter()],
  trackCorrections: true,
  onFieldFocus: (field, timestamp) => {},
  onFieldBlur: (field, timestamp, timeSpentMs) => {},
  onFieldError: (field, error) => {},
  onAbandon: (lastField, filledFields, totalFields) => {},
  onComplete: (totalTimeMs, fieldTimes) => {},
})

Returns:

FieldTypeDescription
fieldAnalyticsMap<string, FieldAnalytics>Per-field metrics
completionRatenumber0-100%
lastFocusedFieldstring | nullLast focused field
totalTimeMsnumberTime since form mount
totalErrorsnumberTotal error count
trackAbandon()() => voidForce abandon event
trackComplete()() => voidForce complete event
reset()() => voidReset all metrics

AnalyticsPanel

Dev-only floating panel showing live metrics:

<AnalyticsPanel
  analytics={analytics}
  position="bottom-right" // bottom-right | bottom-left | top-right | top-left
/>

Import

import { AnalyticsPanel, useFormAnalytics } from '@letar/forms'
import { createUmamiAdapter } from '@letar/forms/analytics'

On this page