Smart Autofill
Automatic HTML autocomplete attributes for better UX and accessibility
Overview
Correct autocomplete attributes improve form conversion by 30% (Google) and satisfy WCAG 1.3.5 for personal data fields. @letar/forms automatically sets autocomplete based on field names — no configuration needed.
Full example
The complete sandbox example, read directly from the form-develop-app source at build time.
'use client'
import { Code, Heading, Text, VStack } from '@chakra-ui/react'
import { Form } from '@letar/forms'
import { useEffect, useRef, useState } from 'react'
import { z } from 'zod/v4'
import { DemoPageLayout } from '../_components'
const ContactSchema = z
.object({
firstName: z
.string()
.min(1, 'Обязательное')
.meta({ ui: { title: 'Имя' } }),
lastName: z
.string()
.min(1, 'Обязательное')
.meta({ ui: { title: 'Фамилия' } }),
email: z
.string()
.email('Некорректный')
.meta({ ui: { title: 'Email' } }),
phone: z
.string()
.optional()
.meta({ ui: { title: 'Телефон' } }),
company: z
.string()
.optional()
.meta({ ui: { title: 'Компания' } }),
address: z
.string()
.optional()
.meta({ ui: { title: 'Адрес' } }),
city: z
.string()
.optional()
.meta({ ui: { title: 'Город' } }),
postalCode: z
.string()
.optional()
.meta({ ui: { title: 'Индекс' } }),
country: z
.string()
.optional()
.meta({ ui: { title: 'Страна' } }),
// Поле без автоопределения — проверяем что не ставится
notes: z
.string()
.optional()
.meta({ ui: { title: 'Заметки' } }),
// Явное отключение через meta
secretCode: z
.string()
.optional()
.meta({ ui: { title: 'Секретный код', autocomplete: 'off' } }),
})
.strip()
/**
* Компонент для отображения autocomplete атрибутов из DOM
*/
function AutocompleteInspector() {
const [attrs, setAttrs] = useState<Array<{ name: string; autocomplete: string | null }>>([])
const timerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
useEffect(() => {
// Даём время на рендер полей
timerRef.current = setTimeout(() => {
const inputs = document.querySelectorAll<HTMLInputElement | HTMLTextAreaElement>(
'input[data-field-name], textarea[data-field-name]',
)
const result = Array.from(inputs).map((input) => ({
name: input.getAttribute('data-field-name') ?? '?',
autocomplete: input.getAttribute('autocomplete'),
}))
setAttrs(result)
}, 500)
return () => clearTimeout(timerRef.current)
}, [])
if (attrs.length === 0) {
return null
}
return (
<VStack gap={1} align="stretch" p={4} bg="bg.subtle" borderRadius="md">
<Heading size="sm" mb={2}>
Autocomplete атрибуты в DOM
</Heading>
{attrs.map((a) => (
<Text key={a.name} fontSize="sm">
<Code>{a.name}</Code> →{' '}
<Code colorPalette={a.autocomplete ? 'green' : 'gray'}>{a.autocomplete ?? 'не установлен'}</Code>
</Text>
))}
</VStack>
)
}
export default function AutofillDemoPage() {
return (
<DemoPageLayout
title="Smart Autofill Demo"
description="Автоматическое проставление autocomplete атрибутов по имени поля. Проверяйте в DevTools."
>
<Form
schema={ContactSchema}
initialValue={{ firstName: '', lastName: '', email: '' }}
onSubmit={async (data) => alert(JSON.stringify(data, null, 2))}
>
<VStack gap={4} align="stretch">
<Form.Field.String name="firstName" />
<Form.Field.String name="lastName" />
<Form.Field.String name="email" />
<Form.Field.String name="phone" />
<Form.Field.String name="company" />
<Form.Field.String name="address" />
<Form.Field.String name="city" />
<Form.Field.String name="postalCode" />
<Form.Field.String name="country" />
<Form.Field.String name="notes" />
<Form.Field.String name="secretCode" />
<Form.Button.Submit>Отправить</Form.Button.Submit>
</VStack>
<AutocompleteInspector />
</Form>
</DemoPageLayout>
)
}shadcn-вариант этого примера ещё не готов.
Vue-пример для этого поля появится позже.
Angular-пример для этого поля появится позже.
How It Works
Fields named email, phone, firstName, etc. automatically receive the correct autocomplete attribute:
<Form schema={Schema} initialValue={data} onSubmit={save}>
<Form.Field.String name="email" /> {/* autocomplete="email" */}
<Form.Field.String name="firstName" /> {/* autocomplete="given-name" */}
<Form.Field.Phone name="phone" /> {/* autocomplete="tel" */}
<Form.Field.Password name="password" /> {/* autocomplete="current-password" */}
<Form.Field.String name="city" /> {/* autocomplete="address-level2" */}
</Form>No props needed — it just works.
Supported Fields
| Field name pattern | autocomplete value |
|---|---|
email, e-mail | email |
phone, tel, mobile | tel |
firstName, first_name | given-name |
lastName, last_name, surname | family-name |
name, fullName | name |
password | current-password |
newPassword, confirmPassword | new-password |
address, street | street-address |
city | address-level2 |
state, region | address-level1 |
zip, postalCode | postal-code |
country | country-name |
company, organization | organization |
username | username |
Override via Schema
Use .meta({ ui: { autocomplete } }) to override auto-detection:
const Schema = z.object({
// Override: use street-address instead of default
deliveryAddress: z.string().meta({
ui: { title: 'Delivery Address', autocomplete: 'street-address' },
}),
// Disable autofill for this field
secretCode: z.string().meta({
ui: { title: 'Secret Code', autocomplete: 'off' },
}),
})Override via Props
The autoComplete prop always wins:
<Form.Field.String name="email" autoComplete="username" />Priority
autoCompleteprop (highest).meta({ ui: { autocomplete } })from Zod schema- Auto-detection from field name
Nested Fields
For nested paths like address.city, auto-detection uses the last segment (city):
<Form.Group name="billing">
<Form.Field.String name="city" /> {/* autocomplete="address-level2" */}
<Form.Field.String name="zip" /> {/* autocomplete="postal-code" */}
</Form.Group>Affected Components
Auto-detection works on:
Form.Field.StringForm.Field.PasswordForm.Field.TextareaForm.Field.Phone(alwaystel)
Live Example
Try the interactive example on forms-example.letar.best.