S
Text Field

Input Group

Text input with prefix and/or suffix elements for formatted inputs.

stable
input group prefix suffix

Interactive demo of input group field

Enter the price in USD

Enter weight in kilograms

Enter your website domain

/**
 * Input Group Field Demo - Interactive examples of input group fields
 */

import { Form, FormBuilder } from '@saastro/forms';

import { FormProvider } from '@/components/FormProvider';
import { TooltipProvider } from '@/components/ui/tooltip';

const config = FormBuilder.create('input-group-demo')
  .layout('manual')
  .columns(12)
  .addField('price', (f) =>
    f
      .type('input-group')
      .label('Price')
      .placeholder('0.00')
      .helperText('Enter the price in USD')
      .required('Price is required')
      .columns({ default: 12, md: 6 }),
  )
  .addField('weight', (f) =>
    f
      .type('input-group')
      .label('Weight')
      .placeholder('0')
      .helperText('Enter weight in kilograms')
      .required()
      .columns({ default: 12, md: 6 }),
  )
  .addField('website', (f) =>
    f
      .type('input-group')
      .label('Website')
      .placeholder('yoursite')
      .helperText('Enter your website domain')
      .optional()
      .columns({ default: 12 }),
  )
  .addStep('main', ['price', 'weight', 'website'])
  .build();

export default function InputGroupDemo() {
  const handleSubmit = (data: Record<string, unknown>) => {
    console.log('Form submitted:', data);
    alert('Form submitted! Check console for data.');
  };

  return (
    <TooltipProvider>
      <FormProvider>
        <Form config={config} onSubmit={handleSubmit} className="space-y-4" />
      </FormProvider>
    </TooltipProvider>
  );
}

Overview

The input group field extends the standard text input with prefix and suffix elements. Use it for formatted inputs like currency, URLs, or measurements.

Usage

With Prefix

import { FormBuilder } from '@saastro/forms';

const config = FormBuilder.create('form')
  .addField('price', (f) =>
    f
      .type('input-group')
      .label('Price')
      .placeholder('0.00')
      // Configure prefix via component props
      .required('Price is required'),
  )
  .addStep('main', ['price'])
  .build();

With Suffix

.addField('weight', (f) =>
  f.type('input-group')
    .label('Weight')
    .placeholder('0')
    // Configure suffix via component props
    .required()
)

With Both

.addField('website', (f) =>
  f.type('input-group')
    .label('Website')
    .placeholder('example')
    // Configure both prefix and suffix
    .optional()
)

JSON Configuration

{
  "type": "input-group",
  "label": "Price",
  "placeholder": "0.00",
  "prefix": "$",
  "schema": { "required": true }
}

Props

PropTypeDefaultDescription
type'input-group'-Field type (required)
labelstring-Label text
placeholderstring-Placeholder text
helperTextstring-Help text below field
prefixstring | ReactNode-Prefix element (configured via registry)
suffixstring | ReactNode-Suffix element (configured via registry)
size'sm' | 'md' | 'lg''md'Input size variant
columnsPartial<Record<Breakpoint, number>>-Grid columns by breakpoint
disabledboolean | function | ConditionGroupfalseDisable the field
hiddenboolean | function | ConditionGroup | ResponsivefalseHide the field

Common Patterns

Currency Input

.addField('amount', (f) =>
  f.type('input-group')
    .label('Amount')
    .placeholder('0.00')
    // Prefix: $ configured in registry
)

URL Input

.addField('domain', (f) =>
  f.type('input-group')
    .label('Domain')
    .placeholder('yoursite')
    // Prefix: https:// Suffix: .com
)

Percentage Input

.addField('discount', (f) =>
  f.type('input-group')
    .label('Discount')
    .placeholder('0')
    // Suffix: %
)

Validation

Required with Number Validation

.addField('price', (f) =>
  f.type('input-group')
    .label('Price')
    .required('Price is required')
    .numberRange(0, 10000)
)

Styling

Custom Classes

.addField('price', (f) =>
  f.type('input-group')
    .label('Price')
    .placeholder('0.00')
    .required()
    .classNames({
      wrapper: 'bg-muted/30 p-4 rounded-lg',
      input: 'border-2 border-primary/20 font-mono',
      label: 'text-lg font-semibold',
      error: 'text-destructive text-sm',
    })
)

Size Variants

.addField('amount', (f) =>
  f.type('input-group')
    .label('Amount')
    .size('lg')
)

Responsive Layout

.addField('price', (f) =>
  f.type('input-group')
    .label('Price')
    .columns({ default: 12, md: 4 })
)

JSON Styling

{
  "type": "input-group",
  "label": "Price",
  "prefix": "$",
  "wrapper_className": "bg-muted/30 p-4 rounded-lg",
  "input_className": "border-2 border-primary/20 font-mono",
  "label_className": "text-lg font-semibold",
  "size": "lg",
  "columns": { "default": 12, "md": 4 }
}

Accessibility

  • Prefix/suffix are decorative, not read separately
  • Main input retains full accessibility
  • Proper labeling maintained