Convert Figma logo to code with AI

gcanti logotcomb-form-native

Forms library for react-native

3,148
457
3,148
125

Top Related Projects

34,040

Build forms in React, without the tears 😭

📋 React Hooks for form state management and validation (Web + React Native)

A Higher Order Component using react-redux to keep form state in a Redux store

🏁 High performance subscription-based form state management for React

4,460

Performance-focused API for React forms 🚀

Quick Overview

tcomb-form-native is a React Native library for building forms. It provides a simple and declarative way to define form structures and validation rules, automatically generating the corresponding UI components and handling form state management.

Pros

  • Reduces boilerplate code for form creation and validation
  • Automatically generates form UI based on schema definitions
  • Supports custom styling and component overrides
  • Integrates well with tcomb for runtime type checking

Cons

  • Limited built-in UI components compared to some other form libraries
  • Learning curve for defining complex form structures
  • Less active development and community support in recent years
  • May require additional setup for more advanced use cases

Code Examples

  1. Basic form definition:
import t from 'tcomb-form-native';

const Person = t.struct({
  name: t.String,
  age: t.Number,
  email: t.String
});

const options = {
  fields: {
    email: {
      keyboardType: 'email-address'
    }
  }
};

const Form = t.form.Form;

<Form
  type={Person}
  options={options}
/>
  1. Custom validation:
const Email = t.refinement(t.String, (email) => {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
});

const User = t.struct({
  username: t.String,
  email: Email
});
  1. Custom styling:
const options = {
  stylesheet: {
    ...Form.stylesheet,
    textbox: {
      ...Form.stylesheet.textbox,
      normal: {
        ...Form.stylesheet.textbox.normal,
        borderColor: 'blue',
        borderRadius: 8
      }
    }
  }
};

Getting Started

  1. Install the library:

    npm install tcomb-form-native
    
  2. Import and use in your React Native component:

    import React from 'react';
    import { View, Button } from 'react-native';
    import t from 'tcomb-form-native';
    
    const Form = t.form.Form;
    
    const User = t.struct({
      name: t.String,
      email: t.String
    });
    
    export default function MyForm() {
      const handleSubmit = () => {
        const value = this._form.getValue();
        console.log(value);
      };
    
      return (
        <View>
          <Form
            ref={c => this._form = c}
            type={User}
          />
          <Button title="Submit" onPress={handleSubmit} />
        </View>
      );
    }
    

Competitor Comparisons

34,040

Build forms in React, without the tears 😭

Pros of Formik

  • More flexible and customizable, allowing for complex form structures
  • Better integration with React ecosystem and modern JavaScript practices
  • Extensive documentation and large community support

Cons of Formik

  • Steeper learning curve for beginners compared to tcomb-form-native
  • Requires more boilerplate code for basic form setups

Code Comparison

tcomb-form-native:

const Form = t.form.Form;
const User = t.struct({
  name: t.String,
  email: t.String
});

<Form type={User} />

Formik:

<Formik
  initialValues={{ name: '', email: '' }}
  onSubmit={values => console.log(values)}
>
  {({ handleChange, handleSubmit, values }) => (
    <Form onSubmit={handleSubmit}>
      <Input name="name" onChange={handleChange} value={values.name} />
      <Input name="email" onChange={handleChange} value={values.email} />
      <Button onPress={handleSubmit} title="Submit" />
    </Form>
  )}
</Formik>

📋 React Hooks for form state management and validation (Web + React Native)

Pros of react-hook-form

  • Lightweight and performant, with minimal re-renders
  • Flexible and easy to integrate with existing React projects
  • Extensive documentation and community support

Cons of react-hook-form

  • Requires more manual setup for complex form structures
  • Less opinionated, which may lead to inconsistent implementations across projects

Code Comparison

react-hook-form:

import { useForm } from 'react-hook-form';

const { register, handleSubmit } = useForm();
const onSubmit = data => console.log(data);

<form onSubmit={handleSubmit(onSubmit)}>
  <input {...register("firstName")} />
  <input type="submit" />
</form>

tcomb-form-native:

import t from 'tcomb-form-native';

const Form = t.form.Form;
const Person = t.struct({
  name: t.String,
  age: t.Number,
});

<Form
  ref="form"
  type={Person}
/>
<Button onPress={this.onPress} title="Save" />

Summary

react-hook-form offers a more modern, flexible approach to form handling in React applications, with better performance and extensive community support. However, it requires more manual setup and can lead to less consistent implementations across projects. tcomb-form-native provides a more structured, opinionated approach but may be less flexible for complex form scenarios. The choice between the two depends on the specific project requirements and developer preferences.

A Higher Order Component using react-redux to keep form state in a Redux store

Pros of redux-form

  • Seamless integration with Redux, allowing for centralized state management
  • Extensive documentation and large community support
  • Highly customizable and flexible for complex form scenarios

Cons of redux-form

  • Steeper learning curve, especially for developers new to Redux
  • Can be overkill for simple form implementations
  • Requires additional setup and boilerplate code

Code Comparison

redux-form:

import { reduxForm, Field } from 'redux-form';

const MyForm = ({ handleSubmit }) => (
  <form onSubmit={handleSubmit}>
    <Field name="username" component="input" type="text" />
    <button type="submit">Submit</button>
  </form>
);

export default reduxForm({ form: 'myForm' })(MyForm);

tcomb-form-native:

import t from 'tcomb-form-native';

const Form = t.form.Form;
const User = t.struct({
  username: t.String,
});

const MyForm = () => (
  <Form
    type={User}
    options={{
      fields: {
        username: {
          label: 'Username',
        },
      },
    }}
  />
);

redux-form offers more control and integration with Redux, while tcomb-form-native provides a simpler, more declarative approach. The choice between the two depends on project requirements, complexity, and developer preferences.

🏁 High performance subscription-based form state management for React

Pros of react-final-form

  • More flexible and customizable form handling
  • Better performance with large forms due to subscription-based updates
  • Extensive documentation and active community support

Cons of react-final-form

  • Steeper learning curve for beginners
  • Requires more boilerplate code for basic forms
  • Less opinionated, which may lead to inconsistent implementations

Code Comparison

tcomb-form-native:

const Form = t.form.Form;
const User = t.struct({
  name: t.String,
  email: t.String
});

<Form type={User} />

react-final-form:

import { Form, Field } from 'react-final-form';

<Form
  onSubmit={onSubmit}
  render={({ handleSubmit }) => (
    <form onSubmit={handleSubmit}>
      <Field name="name" component="input" />
      <Field name="email" component="input" />
    </form>
  )}
/>

react-final-form offers more control over form rendering and validation, while tcomb-form-native provides a more declarative approach with built-in form generation based on defined structures. The choice between the two depends on the project's requirements for flexibility, performance, and ease of use.

4,460

Performance-focused API for React forms 🚀

Pros of unform

  • More flexible and customizable form building
  • Better TypeScript support and type inference
  • Supports multiple UI libraries (React Native, React Web, etc.)

Cons of unform

  • Steeper learning curve due to its flexibility
  • Less opinionated structure compared to tcomb-form-native
  • Requires more setup and configuration

Code Comparison

unform:

import React from 'react';
import { Form } from '@unform/core';
import { Input } from '@unform/web';

const MyForm = () => (
  <Form onSubmit={handleSubmit}>
    <Input name="email" type="email" />
    <Input name="password" type="password" />
    <button type="submit">Submit</button>
  </Form>
);

tcomb-form-native:

import React from 'react';
import t from 'tcomb-form-native';

const Form = t.form.Form;
const User = t.struct({
  email: t.String,
  password: t.String,
});

const MyForm = () => (
  <Form
    type={User}
    options={{
      fields: {
        email: { type: 'email' },
        password: { type: 'password' },
      },
    }}
  />
);

The code comparison shows that unform provides a more React-like component structure, while tcomb-form-native uses a more declarative approach with predefined structures and options.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

build status dependency status npm downloads

Notice

tcomb-form-native is looking for maintainers. If you're interested in helping, a great way to get started would just be to start weighing-in on GitHub issues, reviewing and testing some PRs.

Contents

Setup

npm install tcomb-form-native

Supported react-native versions

VersionReact Native SupportAndroid SupportiOS Support
0.5 - 0.6.10.25.0 - 0.35.07.110.0.2
0.40.20.0 - 0.24.07.110.0.2
0.30.1.0 - 0.13.07.110.0.2
Complies with react-native-version-support-table

Domain Driven Forms

The tcomb library provides a concise but expressive way to define domain models in JavaScript.

The tcomb-validation library builds on tcomb, providing validation functions for tcomb domain models.

This library builds on those two and the awesome react-native.

Benefits

With tcomb-form-native you simply call <Form type={Model} /> to generate a form based on that domain model. What does this get you?

  1. Write a lot less code
  2. Usability and accessibility for free (automatic labels, inline validation, etc)
  3. No need to update forms when domain model changes

JSON Schema support

JSON Schemas are also supported via the (tiny) tcomb-json-schema library.

Note. Please use tcomb-json-schema ^0.2.5.

Pluggable look and feel

The look and feel is customizable via react-native stylesheets and templates (see documentation).

Screencast

http://react.rocks/example/tcomb-form-native

Example App

https://github.com/bartonhammond/snowflake React-Native, Tcomb, Redux, Parse.com, Jest - 88% coverage

Example

// index.ios.js

'use strict';

var React = require('react-native');
var t = require('tcomb-form-native');
var { AppRegistry, StyleSheet, Text, View, TouchableHighlight } = React;

var Form = t.form.Form;

// here we are: define your domain model
var Person = t.struct({
  name: t.String,              // a required string
  surname: t.maybe(t.String),  // an optional string
  age: t.Number,               // a required number
  rememberMe: t.Boolean        // a boolean
});

var options = {}; // optional rendering options (see documentation)

var AwesomeProject = React.createClass({

  onPress: function () {
    // call getValue() to get the values of the form
    var value = this.refs.form.getValue();
    if (value) { // if validation fails, value will be null
      console.log(value); // value here is an instance of Person
    }
  },

  render: function() {
    return (
      <View style={styles.container}>
        {/* display */}
        <Form
          ref="form"
          type={Person}
          options={options}
        />
        <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'>
          <Text style={styles.buttonText}>Save</Text>
        </TouchableHighlight>
      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    justifyContent: 'center',
    marginTop: 50,
    padding: 20,
    backgroundColor: '#ffffff',
  },
  buttonText: {
    fontSize: 18,
    color: 'white',
    alignSelf: 'center'
  },
  button: {
    height: 36,
    backgroundColor: '#48BBEC',
    borderColor: '#48BBEC',
    borderWidth: 1,
    borderRadius: 8,
    marginBottom: 10,
    alignSelf: 'stretch',
    justifyContent: 'center'
  }
});

AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);

Output:

(Labels are automatically generated)

Result

Ouput after a validation error:

Result after a validation error

API

getValue()

Returns null if the validation failed, an instance of your model otherwise.

Note. Calling getValue will cause the validation of all the fields of the form, including some side effects like highlighting the errors.

validate()

Returns a ValidationResult (see tcomb-validation for a reference documentation).

Adding a default value and listen to changes

The Form component behaves like a controlled component:

var Person = t.struct({
  name: t.String,
  surname: t.maybe(t.String)
});

var AwesomeProject = React.createClass({

  getInitialState() {
    return {
      value: {
        name: 'Giulio',
        surname: 'Canti'
      }
    };
  },

  onChange(value) {
    this.setState({value});
  },

  onPress: function () {
    var value = this.refs.form.getValue();
    if (value) {
      console.log(value);
    }
  },

  render: function() {
    return (
      <View style={styles.container}>
        <Form
          ref="form"
          type={Person}
          value={this.state.value}
          onChange={this.onChange}
        />
        <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'>
          <Text style={styles.buttonText}>Save</Text>
        </TouchableHighlight>
      </View>
    );
  }
});

The onChange handler has the following signature:

(raw: any, path: Array<string | number>) => void

where

  • raw contains the current raw value of the form (can be an invalid value for your model)
  • path is the path to the field triggering the change

Warning. tcomb-form-native uses shouldComponentUpdate aggressively. In order to ensure that tcomb-form-native detect any change to type, options or value props you have to change references:

Disable a field based on another field's value

var Type = t.struct({
  disable: t.Boolean, // if true, name field will be disabled
  name: t.String
});

// see the "Rendering options" section in this guide
var options = {
  fields: {
    name: {}
  }
};

var AwesomeProject = React.createClass({

  getInitialState() {
    return {
      options: options,
      value: null
    };
  },

  onChange(value) {
    // tcomb immutability helpers
    // https://github.com/gcanti/tcomb/blob/master/docs/API.md#updating-immutable-instances
    var options = t.update(this.state.options, {
      fields: {
        name: {
          editable: {'$set': !value.disable}
        }
      }
    });
    this.setState({options: options, value: value});
  },

  onPress: function () {
    var value = this.refs.form.getValue();
    if (value) {
      console.log(value);
    }
  },

  render: function() {
    return (
      <View style={styles.container}>
        <Form
          ref="form"
          type={Type}
          options={this.state.options}
          value={this.state.value}
          onChange={this.onChange}
        />
        <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'>
          <Text style={styles.buttonText}>Save</Text>
        </TouchableHighlight>
      </View>
    );
  }

});

How to get access to a field

You can get access to a field with the getComponent(path) API:

var Person = t.struct({
  name: t.String,
  surname: t.maybe(t.String),
  age: t.Number,
  rememberMe: t.Boolean
});

var AwesomeProject = React.createClass({

  componentDidMount() {
    // give focus to the name textbox
    this.refs.form.getComponent('name').refs.input.focus();
  },

  onPress: function () {
    var value = this.refs.form.getValue();
    if (value) {
      console.log(value);
    }
  },

  render: function() {
    return (
      <View style={styles.container}>
        <Form
          ref="form"
          type={Person}
        />
        <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'>
          <Text style={styles.buttonText}>Save</Text>
        </TouchableHighlight>
      </View>
    );
  }
});

How to clear form after submit

var Person = t.struct({
  name: t.String,
  surname: t.maybe(t.String),
  age: t.Number,
  rememberMe: t.Boolean
});

var AwesomeProject = React.createClass({

  getInitialState() {
    return { value: null };
  },

  onChange(value) {
    this.setState({ value });
  },

  clearForm() {
    // clear content from all textbox
    this.setState({ value: null });
  },

  onPress: function () {
    var value = this.refs.form.getValue();
    if (value) {
      console.log(value);
      // clear all fields after submit
      this.clearForm();
    }
  },

  render: function() {
    return (
      <View style={styles.container}>
        <Form
          ref="form"
          type={Person}
          value={this.state.value}
          onChange={this.onChange.bind(this)}
        />
        <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'>
          <Text style={styles.buttonText}>Save</Text>
        </TouchableHighlight>
      </View>
    );
  }
});

Dynamic forms example: how to change a form based on selection

Say I have an iOS Picker, depending on which option is selected in this picker I want the next component to either be a checkbox or a textbox:

const Country = t.enums({
  'IT': 'Italy',
  'US': 'United States'
}, 'Country');

var AwesomeProject = React.createClass({

  // returns the suitable type based on the form value
  getType(value) {
    if (value.country === 'IT') {
      return t.struct({
        country: Country,
        rememberMe: t.Boolean
      });
    } else if (value.country === 'US') {
      return t.struct({
        country: Country,
        name: t.String
      });
    } else {
      return t.struct({
        country: Country
      });
    }
  },

  getInitialState() {
    const value = {};
    return { value, type: this.getType(value) };
  },

  onChange(value) {
    // recalculate the type only if strictly necessary
    const type = value.country !== this.state.value.country ?
      this.getType(value) :
      this.state.type;
    this.setState({ value, type });
  },

  onPress() {
    var value = this.refs.form.getValue();
    if (value) {
      console.log(value);
    }
  },

  render() {

    return (
      <View style={styles.container}>
        <t.form.Form
          ref="form"
          type={this.state.type}
          value={this.state.value}
          onChange={this.onChange}
        />
        <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'>
          <Text style={styles.buttonText}>Save</Text>
        </TouchableHighlight>
      </View>
    );
  }
});

Types

Required field

By default fields are required:

var Person = t.struct({
  name: t.String,    // a required string
  surname: t.String  // a required string
});

Optional field

In order to create an optional field, wrap the field type with the t.maybe combinator:

var Person = t.struct({
  name: t.String,
  surname: t.String,
  email: t.maybe(t.String) // an optional string
});

The postfix " (optional)" is automatically added to optional fields.

You can customise the postfix value or setting a postfix for required fields:

t.form.Form.i18n = {
  optional: '',
  required: ' (required)' // inverting the behaviour: adding a postfix to the required fields
};

Numbers

In order to create a numeric field, use the t.Number type:

var Person = t.struct({
  name: t.String,
  surname: t.String,
  email: t.maybe(t.String),
  age: t.Number // a numeric field
});

tcomb-form-native will convert automatically numbers to / from strings.

Booleans

In order to create a boolean field, use the t.Boolean type:

var Person = t.struct({
  name: t.String,
  surname: t.String,
  email: t.maybe(t.String),
  age: t.Number,
  rememberMe: t.Boolean // a boolean field
});

Booleans are displayed as SwitchIOSs.

Dates

In order to create a date field, use the t.Date type:

var Person = t.struct({
  name: t.String,
  surname: t.String,
  email: t.maybe(t.String),
  age: t.Number,
  birthDate: t.Date // a date field
});

Dates are displayed as DatePickerIOSs under iOS and DatePickerAndroid or TimePickerAndroid under Android, depending on the mode selected (date or time).

Under Android, use the fields option to configure which mode to display the Picker:

// see the "Rendering options" section in this guide
var options = {
  fields: {
    birthDate: {
      mode: 'date' // display the Date field as a DatePickerAndroid
    }
  }
};

iOS date config option

The bundled template will render an iOS UIDatePicker component, but collapsed into a touchable component in order to improve usability. A config object can be passed to customize it with the following parameters:

KeyValue
animationThe animation to collapse the date picker. Defaults to Animated.timing.
animationConfigThe animation configuration object. Defaults to {duration: 200} for the default animation.
formatA (date) => String(date) kind of function to provide a custom date format parsing to display the value. Optional, defaults to (date) => String(date).
defaultValueTextAn string to customize the default value of the null date value text.

For the collapsible customization, look at the dateTouchable and dateValue keys in the stylesheet file.

Android date config option

When using a t.Date type in Android, it can be configured through a config option that take the following parameters:

KeyValue
backgroundDetermines the type of background drawable that's going to be used to display feedback. Optional, defaults to TouchableNativeFeedback.SelectableBackground.
formatA (date) => String(date) kind of function to provide a custom date format parsing to display the value. Optional, defaults to (date) => String(date).
dialogModeDetermines the type of datepicker mode for Android (default, spinner or calendar).
defaultValueTextAn string to customize the default value of the null date value text.

Enums

In order to create an enum field, use the t.enums combinator:

var Gender = t.enums({
  M: 'Male',
  F: 'Female'
});

var Person = t.struct({
  name: t.String,
  surname: t.String,
  email: t.maybe(t.String),
  age: t.Number,
  rememberMe: t.Boolean,
  gender: Gender // enum
});

Enums are displayed as Pickers.

iOS select config option

The bundled template will render an iOS UIPickerView component, but collapsed into a touchable component in order to improve usability. A config object can be passed to customize it with the following parameters:

KeyValue
animationThe animation to collapse the date picker. Defaults to Animated.timing.
animationConfigThe animation configuration object. Defaults to {duration: 200} for the default animation.

For the collapsible customization, look at the pickerContainer, pickerTouchable and pickerValue keys in the stylesheet file.

Refinements

A predicate is a function with the following signature:

(x: any) => boolean

You can refine a type with the t.refinement(type, predicate) combinator:

// a type representing positive numbers
var Positive = t.refinement(t.Number, function (n) {
  return n >= 0;
});

var Person = t.struct({
  name: t.String,
  surname: t.String,
  email: t.maybe(t.String),
  age: Positive, // refinement
  rememberMe: t.Boolean,
  gender: Gender
});

Subtypes allow you to express any custom validation with a simple predicate.

Rendering options

In order to customize the look and feel, use an options prop:

<Form type={Model} options={options} />

Form component

Labels and placeholders

By default labels are automatically generated. You can turn off this behaviour or override the default labels on field basis.

var options = {
  label: 'My struct label' // <= form legend, displayed before the fields
};

var options = {
  fields: {
    name: {
      label: 'My name label' // <= label for the name field
    }
  }
};

In order to automatically generate default placeholders, use the option auto: 'placeholders':

var options = {
  auto: 'placeholders'
};

<Form type={Person} options={options} />

Placeholders

Set auto: 'none' if you don't want neither labels nor placeholders.

var options = {
  auto: 'none'
};

Fields order

You can sort the fields with the order option:

var options = {
  order: ['name', 'surname', 'rememberMe', 'gender', 'age', 'email']
};

Default values

You can set the default values passing a value prop to the Form component:

var value = {
  name: 'Giulio',
  surname: 'Canti',
  age: 41,
  gender: 'M'
};

<Form type={Model} value={value} />

Fields configuration

You can configure each field with the fields option:

var options = {
  fields: {
    name: {
      // name field configuration here..
    },
    surname: {
      // surname field configuration here..
    }
  }
};

Textbox component

Implementation: TextInput

Tech note. Values containing only white spaces are converted to null.

Placeholder

You can set the placeholder with the placeholder option:

var options = {
  fields: {
    name: {
      placeholder: 'Your placeholder here'
    }
  }
};

Label

You can set the label with the label option:

var options = {
  fields: {
    name: {
      label: 'Insert your name'
    }
  }
};

Help message

You can set a help message with the help option:

var options = {
  fields: {
    name: {
      help: 'Your help message here'
    }
  }
};

Help

Error messages

You can add a custom error message with the error option:

var options = {
  fields: {
    email: {
      // you can use strings or JSX
      error: 'Insert a valid email'
    }
  }
};

Help

tcomb-form-native will display the error message when the field validation fails.

error can also be a function with the following signature:

(value, path, context) => ?(string | ReactElement)

where

  • value is an object containing the current form value.
  • path is the path of the value being validated
  • context is the value of the context prop. Also it contains a reference to the component options.

The value returned by the function will be used as error message.

If you want to show the error message onload, add the hasError option:

var options = {
  hasError: true,
  error: <i>A custom error message</i>
};

Another way is to add a:

getValidationErrorMessage(value, path, context)

static function to a type, where:

  • value is the (parsed) current value of the component.
  • path is the path of the value being validated
  • context is the value of the context prop. Also it contains a reference to the component options.
var Age = t.refinement(t.Number, function (n) { return n >= 18; });

// if you define a getValidationErrorMessage function, it will be called on validation errors
Age.getValidationErrorMessage = function (value, path, context) {
  return 'bad age, locale: ' + context.locale;
};

var Schema = t.struct({
  age: Age
});

...

<t.form.Form
  ref="form"
  type={Schema}
  context={{locale: 'it-IT'}}
/>

You can even define getValidationErrorMessage on the supertype in order to be DRY:

t.Number.getValidationErrorMessage = function (value, path, context) {
  return 'bad number';
};

Age.getValidationErrorMessage = function (value, path, context) {
  return 'bad age, locale: ' + context.locale;
};

Other standard options

The following standard options are available (see http://facebook.github.io/react-native/docs/textinput.html):

  • allowFontScaling
  • autoCapitalize
  • autoCorrect
  • autoFocus
  • bufferDelay
  • clearButtonMode
  • editable
  • enablesReturnKeyAutomatically
  • keyboardType
  • maxLength
  • multiline
  • numberOfLines
  • onBlur
  • onEndEditing
  • onFocus
  • onSubmitEditing
  • onContentSizeChange
  • password
  • placeholderTextColor
  • returnKeyType
  • selectTextOnFocus
  • secureTextEntry
  • selectionState
  • textAlign
  • textAlignVertical
  • textContentType
  • underlineColorAndroid

underlineColorAndroid is not supported now on tcomb-form-native due to random crashes on Android, especially on ScrollView. See more on: https://github.com/facebook/react-native/issues/17530#issuecomment-416367184

Checkbox component

Implementation: SwitchIOS

The following options are similar to the Textbox component's ones:

  • label
  • help
  • error

Other standard options

The following standard options are available (see http://facebook.github.io/react-native/docs/switchios.html):

  • disabled
  • onTintColor
  • thumbTintColor
  • tintColor

Select component

Implementation: PickerIOS

The following options are similar to the Textbox component's ones:

  • label
  • help
  • error

nullOption option

You can customize the null option with the nullOption option:

var options = {
  fields: {
    gender: {
      nullOption: {value: '', text: 'Choose your gender'}
    }
  }
};

You can remove the null option setting the nullOption option to false.

Warning: when you set nullOption = false you must also set the Form's value prop for the select field.

Tech note. A value equal to nullOption.value (default '') is converted to null.

Options order

You can sort the options with the order option:

var options = {
  fields: {
    gender: {
      order: 'asc' // or 'desc'
    }
  }
};

Options isCollapsed

You can determinate if Select is collapsed:

var options = {
  fields: {
    gender: {
      isCollapsed: false // default: true
    }
  }
};

If option not set, default is true

Options onCollapseChange

You can set a callback, triggered, when collapse change:

var options = {
  fields: {
    gender: {
      onCollapseChange: () => { console.log('collapse changed'); }
    }
  }
};

DatePicker component

Implementation: DatePickerIOS

Example

var Person = t.struct({
  name: t.String,
  birthDate: t.Date
});

The following options are similar to the Textbox component's ones:

  • label
  • help
  • error

Other standard options

The following standard options are available (see http://facebook.github.io/react-native/docs/datepickerios.html):

  • maximumDate,
  • minimumDate,
  • minuteInterval,
  • mode,
  • timeZoneOffsetInMinutes

Hidden Component

For any component, you can set the field with the hidden option:

var options = {
  fields: {
    name: {
      hidden: true
    }
  }
};

This will completely skip the rendering of the component, while the default value will be available for validation purposes.

Unions

Code Example

const AccountType = t.enums.of([
  'type 1',
  'type 2',
  'other'
], 'AccountType')

const KnownAccount = t.struct({
  type: AccountType
}, 'KnownAccount')

// UnknownAccount extends KnownAccount so it owns also the type field
const UnknownAccount = KnownAccount.extend({
  label: t.String,
}, 'UnknownAccount')

// the union
const Account = t.union([KnownAccount, UnknownAccount], 'Account')

// the final form type
const Type = t.list(Account)

const options = {
  item: [ // one options object for each concrete type of the union
    {
      label: 'KnownAccount'
    },
    {
      label: 'UnknownAccount'
    }
  ]
}

Generally tcomb's unions require a dispatch implementation in order to select the suitable type constructor for a given value and this would be the key in this use case:

// if account type is 'other' return the UnknownAccount type
Account.dispatch = value => value && value.type === 'other' ? UnknownAccount : KnownAccount

Lists

You can handle a list with the t.list combinator:

const Person = t.struct({
  name: t.String,
  tags: t.list(t.String) // a list of strings
});

Items configuration

To configure all the items in a list, set the item option:

const Person = t.struct({
  name: t.String,
  tags: t.list(t.String) // a list of strings
});

const options = {
  fields: { // <= Person options
    tags: {
      item: { // <= options applied to each item in the list
        label: 'My tag'
      }
    }
  }
});

Nested structures

You can nest lists and structs at an arbitrary level:

const Person = t.struct({
  name: t.String,
  surname: t.String
});

const Persons = t.list(Person);

If you want to provide options for your nested structures they must be nested following the type structure. Here is an example:

const Person = t.struct({
  name: t.Struct,
  position: t.Struct({
    latitude: t.Number,
    longitude: t.Number
  });
});

const options = {
  fields: { // <= Person options
    name: {
        label: 'name label'
    }
    position: {
        fields: {
            // Note that latitude is not directly nested in position,
            // but in the fields property
            latitude: {
                label: 'My position label'
            }
        }
    }
  }
});

When dealing with t.list, make sure to declare the fields property inside the item property, as such:

const Documents = t.struct({
  type: t.Number,
  value: t.String
})

const Person = t.struct({
  name: t.Struct,
  documents: t.list(Documents)
});

const options = {
  fields: {
    name: { /*...*/ },
    documents: {
      item: {
        fields: {
          type: {
            // Documents t.struct 'type' options
          },
          value: {
            // Documents t.struct 'value' options
          }
        }
      }
    }
  }
}

Internationalization

You can override the default language (english) with the i18n option:

const options = {
  i18n: {
    optional: ' (optional)',
    required: '',
    add: 'Add',   // add button
    remove: '✘',  // remove button
    up: '↑',      // move up button
    down: '↓'     // move down button
  }
};

Buttons configuration

You can prevent operations on lists with the following options:

  • disableAdd: (default false) prevents adding new items
  • disableRemove: (default false) prevents removing existing items
  • disableOrder: (default false) prevents sorting existing items
const options = {
  disableOrder: true
};

List with Dynamic Items (Different structs based on selected value)

Lists of different types are not supported. This is because a tcomb's list, by definition, contains only values of the same type. You can define a union though:

const AccountType = t.enums.of([
  'type 1',
  'type 2',
  'other'
], 'AccountType')

const KnownAccount = t.struct({
  type: AccountType
}, 'KnownAccount')

// UnknownAccount extends KnownAccount so it owns also the type field
const UnknownAccount = KnownAccount.extend({
  label: t.String,
}, 'UnknownAccount')

// the union
const Account = t.union([KnownAccount, UnknownAccount], 'Account')

// the final form type
const Type = t.list(Account)

Generally tcomb's unions require a dispatch implementation in order to select the suitable type constructor for a given value and this would be the key in this use case:

// if account type is 'other' return the UnknownAccount type
Account.dispatch = value => value && value.type === 'other' ? UnknownAccount : KnownAccount

Customizations

Stylesheets

See also Stylesheet guide.

tcomb-form-native comes with a default style. You can customize the look and feel by setting another stylesheet:

var t = require('tcomb-form-native/lib');
var i18n = require('tcomb-form-native/lib/i18n/en');
var templates = require('tcomb-form-native/lib/templates/bootstrap');

// define a stylesheet (see lib/stylesheets/bootstrap for an example)
var stylesheet = {...};

// override globally the default stylesheet
t.form.Form.stylesheet = stylesheet;
// set defaults
t.form.Form.templates = templates;
t.form.Form.i18n = i18n;

You can also override the stylesheet locally for selected fields:

var Person = t.struct({
  name: t.String
});

var options = {
  fields: {
    name: {
      stylesheet: myCustomStylesheet
    }
  }
};

Or per form:

var Person = t.struct({
  name: t.String
});

var options = {
  stylesheet: myCustomStylesheet
};

For a complete example see the default stylesheet https://github.com/gcanti/tcomb-form-native/blob/master/lib/stylesheets/bootstrap.js.

Templates

tcomb-form-native comes with a default layout, i.e. a bunch of templates, one for each component. When changing the stylesheet is not enough, you can customize the layout by setting custom templates:

var t = require('tcomb-form-native/lib');
var i18n = require('tcomb-form-native/lib/i18n/en');
var stylesheet = require('tcomb-form-native/lib/stylesheets/bootstrap');

// define the templates (see lib/templates/bootstrap for an example)
var templates = {...};

// override globally the default layout
t.form.Form.templates = templates;
// set defaults
t.form.Form.stylesheet = stylesheet;
t.form.Form.i18n = i18n;

You can also override the template locally:

var Person = t.struct({
  name: t.String
});

function myCustomTemplate(locals) {

  var containerStyle = {...};
  var labelStyle = {...};
  var textboxStyle = {...};

  return (
    <View style={containerStyle}>
      <Text style={labelStyle}>{locals.label}</Text>
      <TextInput style={textboxStyle} />
    </View>
  );
}

var options = {
  fields: {
    name: {
      template: myCustomTemplate
    }
  }
};

A template is a function with the following signature:

(locals: Object) => ReactElement

where locals is an object contaning the "recipe" for rendering the input and it's built for you by tcomb-form-native. Let's see an example: the locals object passed in the checkbox template:

type Message = string | ReactElement

{
  stylesheet: Object, // the styles to be applied
  hasError: boolean,  // true if there is a validation error
  error: ?Message,    // the optional error message to be displayed
  label: Message,     // the label to be displayed
  help: ?Message,     // the optional help message to be displayed
  value: boolean,     // the current value of the checkbox
  onChange: Function, // the event handler to be called when the value changes
  config: Object,     // an optional object to pass configuration options to the new template

  ...other input options here...

}

For a complete example see the default template https://github.com/gcanti/tcomb-form-native/blob/master/lib/templates/bootstrap.

i18n

tcomb-form-native comes with a default internationalization (English). You can change it by setting another i18n object:

var t = require('tcomb-form-native/lib');
var templates = require('tcomb-form-native/lib/templates/bootstrap');

// define an object containing your translations (see tcomb-form-native/lib/i18n/en for an example)
var i18n = {...};

// override globally the default i18n
t.form.Form.i18n = i18n;
// set defaults
t.form.Form.templates = templates;
t.form.Form.stylesheet = stylesheet;

Transformers

Say you want a search textbox which accepts a list of keywords separated by spaces:

var Search = t.struct({
  search: t.list(t.String)
});

tcomb-form by default will render the search field as a list. In order to render a textbox you have to override the default behaviour with the factory option:

var options = {
  fields: {
    search: {
      factory: t.form.Textbox
    }
  }
};

There is a problem though: a textbox handle only strings so we need a way to transform a list in a string and a string in a list: a Transformer deals with serialization / deserialization of data and has the following interface:

var Transformer = t.struct({
  format: t.Function, // from value to string, it must be idempotent
  parse: t.Function   // from string to value
});

A basic transformer implementation for the search textbox:

var listTransformer = {
  format: function (value) {
    return Array.isArray(value) ? value.join(' ') : value;
  },
  parse: function (str) {
    return str ? str.split(' ') : [];
  }
};

Now you can handle lists using the transformer option:

// example of initial value
var value = {
  search: ['climbing', 'yosemite']
};

var options = {
  fields: {
    search: {
      factory: t.form.Textbox, // tell tcomb-react-native to use the same component for textboxes
      transformer: listTransformer,
      help: 'Keywords are separated by spaces'
    }
  }
};

Custom factories

You can pack together style, template (and transformers) in a custom component and then you can use it with the factory option:

var Component = t.form.Component;

// extend the base Component
class MyComponent extends Component {

  // this is the only required method to implement
  getTemplate() {
    // define here your custom template
    return function (locals) {

      //return ... jsx ...

    };
  }

  // you can optionally override the default getLocals method
  // it will provide the locals param to your template
  getLocals() {

    // in locals you'll find the default locals:
    // - path
    // - error
    // - hasError
    // - label
    // - onChange
    // - stylesheet
    var locals = super.getLocals();

    // add here your custom locals

    return locals;
  }


}

// as example of transformer: this is the default transformer for textboxes
MyComponent.transformer = {
  format: value => Nil.is(value) ? null : value,
  parse: value => (t.String.is(value) && value.trim() === '') || Nil.is(value) ? null : value
};

var Person = t.struct({
  name: t.String
});

var options = {
  fields: {
    name: {
      factory: MyComponent
    }
  }
};

Tests

npm test

Note: If you are using Jest, you will encounter an error which can be fixed w/ a small change to the package.json.

The error will look similiar to the following:

Error: Cannot find module './datepicker' from 'index.js' at
Resolver.resolveModule

A completely working example jest setup is shown below w/ the http://facebook.github.io/jest/docs/api.html#modulefileextensions-array-string fix added:

  "jest": {
    "setupEnvScriptFile": "./node_modules/react-native/jestSupport/env.js",
    "haste": {
      "defaultPlatform": "ios",
      "platforms": [
        "ios",
        "android"
      ],
      "providesModuleNodeModules": [
        "react-native"
      ]
    },
    "testPathIgnorePatterns": [
      "/node_modules/"
    ],
    "testFileExtensions": [
      "es6",
      "js"
    ],
    "moduleFileExtensions": [
      "js",
      "json",
      "es6",
      "ios.js"    <<<<<<<<<<<< this needs to be defined!
    ],
    "unmockedModulePathPatterns": [
      "react",
      "react-addons-test-utils",
      "react-native-router-flux",
      "promise",
      "source-map",
      "key-mirror",
      "immutable",
      "fetch",
      "redux",
      "redux-thunk",
      "fbjs"
    ],
    "collectCoverage": false,
    "verbose": true
    },

License

MIT

NPM DownloadsLast 30 Days