@letar/forms

Survey Fields

ImageChoice, Likert, and YesNo fields for surveys, questionnaires, and feedback forms

Full example

The complete sandbox example, read directly from the form-develop-app / form-develop-app-shadcn source at build time. The shadcn side (survey-demo) covers ImageChoice and Likert; YesNo lives in a separate auth-fields demo not covered here.

apps/form-develop-app/src/app/survey-fields-demo/page.tsx
'use client'

import { Box, Heading, VStack } from '@chakra-ui/react'
import { Form } from '@letar/forms'
import { useState } from 'react'
import { DemoPageLayout } from '../_components'

export default function SurveyFieldsDemoPage() {
  const [submittedData, setSubmittedData] = useState<Record<string, unknown> | null>(null)

  return (
    <DemoPageLayout title="Survey Fields" description="ImageChoice, Likert, YesNo — поля для опросников и анкет">
      <VStack gap={8} align="stretch">
        {/* ImageChoice */}
        <Box>
          <Heading size="md" mb={3}>
            ImageChoice — выбор из картинок
          </Heading>
          <Form
            debug
            initialValue={{ style: '' }}
            onSubmit={(data) => setSubmittedData(data as Record<string, unknown>)}
          >
            <Form.Field.ImageChoice
              name="style"
              label="Выберите стиль интерьера"
              options={[
                {
                  value: 'modern',
                  label: 'Современный',
                  image: 'https://picsum.photos/seed/modern/300/200',
                  description: 'Минимализм и чистые линии',
                },
                {
                  value: 'classic',
                  label: 'Классический',
                  image: 'https://picsum.photos/seed/classic/300/200',
                  description: 'Элегантность и традиции',
                },
                {
                  value: 'loft',
                  label: 'Лофт',
                  image: 'https://picsum.photos/seed/loft/300/200',
                  description: 'Индустриальный шик',
                },
              ]}
              columns={3}
            />
            <Form.Button.Submit>Выбрать</Form.Button.Submit>
          </Form>
        </Box>

        {/* Likert */}
        <Box>
          <Heading size="md" mb={3}>
            Likert — шкала согласия
          </Heading>
          <Form
            debug
            initialValue={{ experience: undefined }}
            onSubmit={(data) => setSubmittedData(data as Record<string, unknown>)}
          >
            <Form.Field.Likert
              name="experience"
              label="Как вы оцениваете опыт работы с нашим продуктом?"
              anchors={['Совсем не доволен', 'Не доволен', 'Нейтрально', 'Доволен', 'Очень доволен']}
              showNumbers
            />
            <Form.Button.Submit>Отправить</Form.Button.Submit>
          </Form>
        </Box>

        {/* YesNo — buttons */}
        <Box>
          <Heading size="md" mb={3}>
            YesNo — бинарный выбор
          </Heading>
          <Form
            debug
            initialValue={{ agree: undefined, recommend: undefined, subscribe: undefined }}
            onSubmit={(data) => setSubmittedData(data as Record<string, unknown>)}
          >
            <Form.Field.YesNo
              name="agree"
              label="Вы согласны с условиями использования?"
              yesLabel="Да, согласен"
              noLabel="Нет, отказываюсь"
              variant="buttons"
            />
            <Form.Field.YesNo name="recommend" label="Порекомендуете нас друзьям?" variant="thumbs" />
            <Form.Field.YesNo name="subscribe" label="Подписаться на рассылку?" variant="emoji" />
            <Form.Button.Submit>Отправить</Form.Button.Submit>
          </Form>
        </Box>

        {submittedData && (
          <Box p={4} bg="bg.subtle" borderRadius="md">
            <Heading size="sm" mb={2}>
              Отправленные данные:
            </Heading>
            <pre style={{ fontSize: '12px', overflow: 'auto' }}>{JSON.stringify(submittedData, null, 2)}</pre>
          </Box>
        )}
      </VStack>
    </DemoPageLayout>
  )
}

ImageChoice

Visual selection from image cards. Ideal for choosing styles, products, or categories.

<Form.Field.ImageChoice
  name="style"
  label="Choose a style"
  options={[
    { value: 'modern', label: 'Modern', image: '/img/modern.jpg' },
    { value: 'classic', label: 'Classic', image: '/img/classic.jpg' },
  ]}
  columns={3}
  multiple={false}
/>

Value: string (single) or string[] (when multiple={true})

PropTypeDefaultDescription
optionsImageChoiceOption[]requiredOptions with images
columnsnumber3Grid columns (responsive: 1→2→N)
multiplebooleanfalseMulti-select mode

Likert

Agreement/satisfaction scale with labeled points. Standard for NPS and survey forms.

<Form.Field.Likert
  name="satisfaction"
  label="How satisfied are you?"
  anchors={['Very Unsatisfied', 'Unsatisfied', 'Neutral', 'Satisfied', 'Very Satisfied']}
  showNumbers={true}
/>

Value: number (1-based index of selected point)

PropTypeDefaultDescription
anchorsstring[]requiredLabels for each point
showNumbersbooleanfalseShow point numbers

On mobile, the horizontal scale switches to a vertical list.

YesNo

Binary choice with large clickable blocks. Good for consents, confirmations, and simple questions.

<Form.Field.YesNo
  name="agree"
  label="Do you agree?"
  yesLabel="Yes, I agree"
  noLabel="No, I decline"
  variant="buttons"
/>

Value: boolean

PropTypeDefaultDescription
yesLabelstring'Да'Yes button text
noLabelstring'Нет'No button text
variant'buttons' | 'thumbs' | 'emoji''buttons'Visual style

Variants: buttons (text only), thumbs (with thumbs up/down), emoji (with happy/sad face).


Live Example

Try the interactive example on forms-example.letar.best.

On this page