Inputs


TextInput

A single-line text field with label, error, hint, and icon support.

PropTypeDefaultDescription
valuestringControlled value
onChangeText(v: string) => voidChange handler
label?stringField label rendered above the input
error?stringError message rendered below the input
hint?stringHelper text rendered below the input
disabled?booleanfalseDisables the field
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Field height and font size
variant?'outline' | 'filled' | 'underline''outline'Visual style
clearable?booleanfalseShow a clear button when value is non-empty
iconLeft?React.ReactElementIcon rendered on the left side
iconRight?React.ReactElementIcon rendered on the right side
keyboardType?KeyboardTypeOptionsNative keyboard type
secureTextEntry?booleanfalseMask input (passwords)

Variants

import { TextInput } from '@objectifthunes/limestone-sdk';

// Outline (default)
<TextInput value={value} onChangeText={setValue} label="Email" variant="outline" />

// Filled — solid background, no border
<TextInput value={value} onChangeText={setValue} label="Password" variant="filled" secureTextEntry />

// Underline — bottom border only
<TextInput value={value} onChangeText={setValue} label="Username" variant="underline" />

Sizes

<TextInput value={v} onChangeText={setV} label="Extra Small" size="xs" />
<TextInput value={v} onChangeText={setV} label="Small"       size="sm" />
<TextInput value={v} onChangeText={setV} label="Medium"      size="md" />
<TextInput value={v} onChangeText={setV} label="Large"       size="lg" />
<TextInput value={v} onChangeText={setV} label="Extra Large" size="xl" />

With icons and error

import { TextInput, Icon } from '@objectifthunes/limestone-sdk';

<TextInput
  value={email}
  onChangeText={setEmail}
  label="Email address"
  error={errors.email}
  hint="We'll never share your email."
  keyboardType="email-address"
  iconLeft={<Icon name="mail" size="sm" color="mutedForeground" />}
  clearable
/>

A debounced search input with optional cancel button.

PropTypeDefaultDescription
valuestringControlled value
onChangeText(v: string) => voidImmediate change handler
onDebouncedChange?(v: string) => voidFires after debounceMs of inactivity
debounceMs?number300Debounce delay in milliseconds
placeholder?string'Search'Placeholder text
showCancel?booleanfalseShow a Cancel button to clear and blur
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Bar height and font size
import { SearchBar } from '@objectifthunes/limestone-sdk';
import { useState } from 'react';

function ProductSearch() {
  const [query, setQuery] = useState('');

  return (
    <SearchBar
      value={query}
      onChangeText={setQuery}
      onDebouncedChange={(q) => fetchProducts(q)}
      debounceMs={400}
      placeholder="Search products…"
      showCancel
    />
  );
}

Select

A modal-based picker supporting single and multi-select modes with optional search.

PropTypeDefaultDescription
optionsSelectOption[]Array of { label, value, disabled? }
value?stringSelected value (single mode)
values?string[]Selected values (multi mode)
onValueChange?(v: string) => voidSingle mode change handler
onValuesChange?(v: string[]) => voidMulti mode change handler
multiple?booleanfalseEnable multi-select
searchable?booleanfalseShow a search field inside the picker
placeholder?stringTrigger button placeholder text
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Trigger button size
variant?'outline' | 'filled' | 'underline''outline'Trigger button variant
import { Select } from '@objectifthunes/limestone-sdk';
import type { SelectOption } from '@objectifthunes/limestone-sdk';

const COUNTRY_OPTIONS: SelectOption[] = [
  { label: 'United States', value: 'us' },
  { label: 'France', value: 'fr' },
  { label: 'Germany', value: 'de' },
];

// Single select
function CountryPicker() {
  const [country, setCountry] = useState('');

  return (
    <Select
      options={COUNTRY_OPTIONS}
      value={country}
      onValueChange={setCountry}
      placeholder="Select a country"
      searchable
    />
  );
}

// Multi-select
function TagPicker() {
  const [tags, setTags] = useState<string[]>([]);

  return (
    <Select
      options={TAG_OPTIONS}
      values={tags}
      onValuesChange={setTags}
      multiple
      placeholder="Select tags"
    />
  );
}

Switch

A toggle switch with optional label.

PropTypeDefaultDescription
valuebooleanControlled state
onValueChange(v: boolean) => voidChange handler
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Switch size
trackColor?{ on: string; off: string }Custom track colors
thumbColor?stringCustom thumb color
label?stringLabel text
labelPosition?'left' | 'right''right'Label side
import { Switch } from '@objectifthunes/limestone-sdk';

function NotificationToggle() {
  const [enabled, setEnabled] = useState(true);

  return (
    <Switch
      value={enabled}
      onValueChange={setEnabled}
      label="Push notifications"
      labelPosition="left"
    />
  );
}

Checkbox and CheckboxGroup

Checkbox is a standalone checked/unchecked control. CheckboxGroup manages a set of CheckboxGroupItem children by value.

Checkbox props

PropTypeDefaultDescription
checkedbooleanControlled state
onCheckedChange(v: boolean) => voidChange handler
size?SpacingProp'md'Box size
variant?'filled' | 'outline''filled'Visual style
label?stringLabel text
labelPosition?'left' | 'right''right'Label side

CheckboxGroup props

PropTypeDefaultDescription
valuesstring[]Array of currently checked values
onValuesChange(v: string[]) => voidChange handler
direction?'horizontal' | 'vertical''vertical'Layout direction
gap?SpacingPropGap between items
import { Checkbox, CheckboxGroup, CheckboxGroupItem } from '@objectifthunes/limestone-sdk';
import { useState } from 'react';

// Standalone
function AcceptTerms() {
  const [accepted, setAccepted] = useState(false);

  return (
    <Checkbox
      checked={accepted}
      onCheckedChange={setAccepted}
      label="I accept the terms and conditions"
    />
  );
}

// Group
function PermissionsForm() {
  const [perms, setPerms] = useState<string[]>(['camera']);

  return (
    <CheckboxGroup values={perms} onValuesChange={setPerms} gap="sm">
      <CheckboxGroupItem value="camera" label="Camera" />
      <CheckboxGroupItem value="microphone" label="Microphone" />
      <CheckboxGroupItem value="location" label="Location" />
    </CheckboxGroup>
  );
}

RadioGroup and RadioItem

A controlled single-select group. RadioGroup provides context; RadioItem renders each option.

RadioGroup props

PropTypeDefaultDescription
valuestringCurrently selected value
onValueChange(v: string) => voidChange handler
direction?'horizontal' | 'vertical''vertical'Layout direction
gap?SpacingPropGap between items

RadioItem props

PropTypeDefaultDescription
valuestringOption value
label?stringOption label
labelPosition?'left' | 'right''right'Label side
size?SpacingProp'md'Radio circle size
import { RadioGroup, RadioItem } from '@objectifthunes/limestone-sdk';

function ThemePicker() {
  const [theme, setTheme] = useState('light');

  return (
    <RadioGroup value={theme} onValueChange={setTheme} gap="sm">
      <RadioItem value="light" label="Light" />
      <RadioItem value="dark" label="Dark" />
      <RadioItem value="system" label="System default" />
    </RadioGroup>
  );
}

Slider

A range slider with optional value display.

PropTypeDefaultDescription
valuenumberControlled value
minnumberMinimum value
maxnumberMaximum value
step?number1Step increment
onValueChange(v: number) => voidChange handler
trackHeight?numberTrack bar height in pixels
thumbSize?numberThumb circle diameter
minimumTrackColor?stringColor for the filled portion
maximumTrackColor?stringColor for the unfilled portion
thumbColor?stringThumb color
showValue?booleanfalseDisplay current value above thumb
formatValue?(v: number) => stringCustom value formatter
import { Slider } from '@objectifthunes/limestone-sdk';

function VolumeControl() {
  const [volume, setVolume] = useState(75);

  return (
    <Slider
      value={volume}
      min={0}
      max={100}
      step={5}
      onValueChange={setVolume}
      showValue
      formatValue={(v) => `${v}%`}
    />
  );
}

OTPInput

A one-time password input rendered as individual digit boxes. secure mode renders each digit as a dot (PinInput style).

PropTypeDefaultDescription
length?number6Number of digit boxes
value?stringControlled value
onComplete?(v: string) => voidFires when all boxes are filled
secure?booleanfalseMask digits (PinInput mode)
keyboardType?KeyboardTypeOptions'number-pad'Keyboard type
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Box size
gap?SpacingProp'sm'Gap between boxes
variant?'outline' | 'filled' | 'underline''outline'Box style
import { OTPInput } from '@objectifthunes/limestone-sdk';

// Normal OTP
function VerifyEmail() {
  return (
    <OTPInput
      length={6}
      onComplete={(code) => verifyCode(code)}
    />
  );
}

// Secure PinInput mode
function SetPin() {
  return (
    <OTPInput
      length={4}
      secure
      onComplete={(pin) => savePin(pin)}
    />
  );
}

PhoneInput

A phone number field with country code picker.

PropTypeDefaultDescription
value?stringE.164-formatted phone number, e.g. '+14155552671'
onValueChange?(v: string) => voidChange handler (E.164 string)
countries?Country[]allCountriesCountry list for the picker
defaultCountry?string'US'ISO 3166-1 alpha-2 default
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Field size
variant?'outline' | 'filled' | 'underline''outline'Field variant
import { PhoneInput, allCountries, popularCountries } from '@objectifthunes/limestone-sdk';

// With all countries
function SignupPhone() {
  const [phone, setPhone] = useState('');

  return (
    <PhoneInput
      value={phone}
      onValueChange={setPhone}
      countries={allCountries}
      defaultCountry="FR"
    />
  );
}

// With a shortened popular-country list
function QuickPhone() {
  return (
    <PhoneInput
      onValueChange={setPhone}
      countries={popularCountries}
    />
  );
}

SegmentedControl

A horizontal tab-style selector for mutually exclusive options.

PropTypeDefaultDescription
segmentsSegment[]Array of { label, value, disabled? }
valuestringCurrently selected value
onValueChange(v: string) => voidChange handler
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Control height
import { SegmentedControl } from '@objectifthunes/limestone-sdk';
import type { Segment } from '@objectifthunes/limestone-sdk';

const VIEW_SEGMENTS: Segment[] = [
  { label: 'List', value: 'list' },
  { label: 'Grid', value: 'grid' },
  { label: 'Map', value: 'map' },
];

function ViewToggle() {
  const [view, setView] = useState('list');

  return (
    <SegmentedControl
      segments={VIEW_SEGMENTS}
      value={view}
      onValueChange={setView}
    />
  );
}

DatePicker

A date, time, or datetime picker that opens a native OS picker modal.

PropTypeDefaultDescription
value?DateControlled date value
onValueChange?(d: Date) => voidChange handler
mode?'date' | 'time' | 'datetime''date'Picker mode
minimumDate?DateEarliest selectable date
maximumDate?DateLatest selectable date
displayFormat?stringFormat string for the trigger button label
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Trigger button size
variant?'outline' | 'filled' | 'underline''outline'Trigger button variant
import { DatePicker } from '@objectifthunes/limestone-sdk';

function EventScheduler() {
  const [date, setDate] = useState<Date>(new Date());

  return (
    <DatePicker
      value={date}
      onValueChange={setDate}
      mode="datetime"
      minimumDate={new Date()}
      displayFormat="MMM d, yyyy h:mm a"
    />
  );
}

Chip

A compact, pressable label used for filters, tags, and selections. Renders with an active/inactive state and an optional dismiss button.

PropTypeDefaultDescription
labelstringChip text
selected?booleanfalseWhether the chip is in the active state
onPress?() => voidCalled when the chip is tapped
onDismiss?() => voidWhen provided, a dismiss × button is rendered
icon?stringIcon name rendered to the left of the label
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''sm'Chip height and font size
variant?'default' | 'primary' | 'secondary''default'Color scheme
disabled?booleanfalseDisables interaction
accessibilityLabel?stringAccessibility label
testID?stringTest identifier
import { Chip, Stack } from '@objectifthunes/limestone-sdk';
import { useState } from 'react';

const CATEGORIES = ['Design', 'Engineering', 'Marketing', 'Sales'];

function CategoryFilter() {
  const [selected, setSelected] = useState<string[]>([]);

  const toggle = (cat: string) =>
    setSelected((prev) =>
      prev.includes(cat) ? prev.filter((c) => c !== cat) : [...prev, cat],
    );

  return (
    <Stack direction="horizontal" gap="sm" wrap>
      {CATEGORIES.map((cat) => (
        <Chip
          key={cat}
          label={cat}
          selected={selected.includes(cat)}
          onPress={() => toggle(cat)}
        />
      ))}
    </Stack>
  );
}

ChipInput

A text field that converts each confirmed entry into an inline Chip. Supports keyboard submission and backspace-to-delete.

PropTypeDefaultDescription
valuesstring[]Controlled array of chip values
onValuesChange(v: string[]) => voidCalled when a chip is added or removed
placeholder?stringInput placeholder shown when no chips are present
maxValues?numberMaximum number of chips allowed
validate?(v: string) => booleanReturn false to reject an entry
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Field height
variant?'outline' | 'filled' | 'underline''outline'Visual style
accessibilityLabel?stringAccessibility label
testID?stringTest identifier
import { ChipInput } from '@objectifthunes/limestone-sdk';
import { useState } from 'react';

function TagsField() {
  const [tags, setTags] = useState<string[]>([]);

  return (
    <ChipInput
      values={tags}
      onValuesChange={setTags}
      placeholder="Add a tag…"
      maxValues={10}
      validate={(v) => v.length >= 2}
    />
  );
}

QuantityPicker

A numeric stepper with decrement and increment buttons. Enforces min/max bounds and supports a custom step.

PropTypeDefaultDescription
valuenumberControlled value
onValueChange(v: number) => voidChange handler
min?number0Minimum allowed value
max?numberMaximum allowed value
step?number1Increment/decrement step
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Control height
disabled?booleanfalseDisables both buttons
formatValue?(v: number) => stringCustom display formatter
accessibilityLabel?stringAccessibility label
testID?stringTest identifier
import { QuantityPicker } from '@objectifthunes/limestone-sdk';
import { useState } from 'react';

function CartItem({ productId }: { productId: string }) {
  const [qty, setQty] = useState(1);

  return (
    <QuantityPicker
      value={qty}
      onValueChange={setQty}
      min={1}
      max={99}
    />
  );
}

Autocomplete

A text input with a dropdown list of suggestions that filter as the user types. Supports free-text entry (combo-box mode) or constrained selection.

PropTypeDefaultDescription
optionsSelectOption[]Full list of { label, value } options
value?stringControlled selected value
onValueChange?(v: string) => voidCalled when the user picks a suggestion
inputValue?stringControlled text input value (unfiltered)
onInputChange?(v: string) => voidCalled on every keystroke
placeholder?stringInput placeholder
maxSuggestions?number5Maximum rows shown in the dropdown
freeInput?booleanfalseAllow values not in options
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Input size
variant?'outline' | 'filled' | 'underline''outline'Input variant
accessibilityLabel?stringAccessibility label
testID?stringTest identifier
import { Autocomplete } from '@objectifthunes/limestone-sdk';
import type { SelectOption } from '@objectifthunes/limestone-sdk';
import { useState } from 'react';

const CITIES: SelectOption[] = [
  { label: 'Paris', value: 'CDG' },
  { label: 'New York', value: 'JFK' },
  { label: 'Tokyo', value: 'NRT' },
  { label: 'London', value: 'LHR' },
];

function CityPicker() {
  const [city, setCity] = useState('');

  return (
    <Autocomplete
      options={CITIES}
      value={city}
      onValueChange={setCity}
      placeholder="Search cities…"
      maxSuggestions={4}
    />
  );
}

Calendar

A full-month calendar grid for date selection, date-range selection, and event display. Supports single and range modes with optional event dot indicators.

PropTypeDefaultDescription
value?DateSelected date (single mode)
startDate?DateRange start date (range mode)
endDate?DateRange end date (range mode)
mode?'single' | 'range' | 'multiple''single'Selection mode
selectedDates?Date[]Selected dates (multiple mode)
onValueChange?(date: Date) => voidCalled on date tap (single mode)
onRangeChange?(start: Date, end: Date | null) => voidCalled on date tap (range mode)
onDatesChange?(dates: Date[]) => voidCalled on date tap (multiple mode)
events?CalendarEvent[]Array of { date: Date; color?: ColorProp } dot markers
minimumDate?DateEarliest selectable date
maximumDate?DateLatest selectable date
disabledDates?Date[]Dates that cannot be selected
firstDayOfWeek?0 | 100 = Sunday, 1 = Monday
showWeekNumbers?booleanfalseRender ISO week numbers in the left gutter
size?'sm' | 'md' | 'lg''md'Cell size
accessibilityLabel?stringAccessibility label
testID?stringTest identifier
import { Calendar } from '@objectifthunes/limestone-sdk';
import { useState } from 'react';

// Single date picker
function AppointmentPicker() {
  const [date, setDate] = useState<Date>(new Date());

  return (
    <Calendar
      value={date}
      onValueChange={setDate}
      minimumDate={new Date()}
    />
  );
}

// Date range picker
function StayDates() {
  const [start, setStart] = useState<Date | null>(null);
  const [end, setEnd] = useState<Date | null>(null);

  return (
    <Calendar
      mode="range"
      startDate={start ?? undefined}
      endDate={end ?? undefined}
      onRangeChange={(s, e) => { setStart(s); setEnd(e); }}
    />
  );
}

// Calendar with event dots
const myEvents = [
  { date: new Date(2026, 3, 5), color: 'primary' },
  { date: new Date(2026, 3, 12), color: 'success' },
];

<Calendar
  value={selectedDate}
  onValueChange={setSelectedDate}
  events={myEvents}
  firstDayOfWeek={1}
/>

SignaturePad

A freehand signature capture canvas. The user draws with their finger; the result can be exported as a base64 PNG or SVG path string.

PropTypeDefaultDescription
onSave(data: SignatureData) => voidCalled when the user taps the save/confirm button; receives { png: string; svg: string }
onClear?() => voidCalled when the pad is cleared
strokeColor?string'#000000'Ink stroke color
strokeWidth?number2.5Ink stroke width in pixels
backgroundColor?string'#ffffff'Canvas background color
height?number200Canvas height in pixels
placeholder?string'Sign here'Placeholder text shown on an empty canvas
showClearButton?booleantrueRender a clear button below the canvas
showSaveButton?booleantrueRender a save/confirm button below the canvas
clearLabel?string'Clear'Label for the clear button
saveLabel?string'Confirm'Label for the save button
accessibilityLabel?stringAccessibility label
testID?stringTest identifier
import { SignaturePad } from '@objectifthunes/limestone-sdk';
import type { SignatureData } from '@objectifthunes/limestone-sdk';

function ConsentForm({ onSigned }: { onSigned: (png: string) => void }) {
  return (
    <SignaturePad
      strokeColor="#1a1a2e"
      strokeWidth={3}
      height={180}
      placeholder="Draw your signature"
      onSave={({ png }) => onSigned(png)}
    />
  );
}