Gathering detailed insights and metrics for tcomb-form-native
Gathering detailed insights and metrics for tcomb-form-native
Gathering detailed insights and metrics for tcomb-form-native
Gathering detailed insights and metrics for tcomb-form-native
tcomb-form-native-templates-material
Material UI templates for `tcomb-form-native`
tcomb-form-native-builder-components
Type for `tcomb-form-native` to show static images
tcomb-form-native-builder
Forms builder library for React Native
tcomb-form-native-builder-utils
Utilities for tcomb-form-native-builder and builder-components
npm install tcomb-form-native
Typescript
Module System
Node Version
NPM Version
76.7
Supply Chain
99.6
Quality
75.7
Maintenance
100
Vulnerability
100
License
JavaScript (100%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
3,135 Stars
239 Commits
458 Forks
63 Watchers
7 Branches
43 Contributors
Updated on Jul 12, 2025
Latest Version
0.6.20
Package Id
tcomb-form-native@0.6.20
Unpacked Size
185.46 kB
Size
49.40 kB
File Count
22
NPM Version
6.4.1
Node Version
9.11.1
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
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.
npm install tcomb-form-native
Version | React Native Support | Android Support | iOS Support |
---|---|---|---|
0.5 - 0.6.1 | 0.25.0 - 0.35.0 | 7.1 | 10.0.2 |
0.4 | 0.20.0 - 0.24.0 | 7.1 | 10.0.2 |
0.3 | 0.1.0 - 0.13.0 | 7.1 | 10.0.2 |
Complies with react-native-version-support-table |
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.
With tcomb-form-native you simply call <Form type={Model} />
to generate a form based on that domain model. What does this get you?
JSON Schemas are also supported via the (tiny) tcomb-json-schema library.
Note. Please use tcomb-json-schema ^0.2.5.
The look and feel is customizable via react-native stylesheets and templates (see documentation).
http://react.rocks/example/tcomb-form-native
https://github.com/bartonhammond/snowflake React-Native, Tcomb, Redux, Parse.com, Jest - 88% coverage
1// index.ios.js 2 3'use strict'; 4 5var React = require('react-native'); 6var t = require('tcomb-form-native'); 7var { AppRegistry, StyleSheet, Text, View, TouchableHighlight } = React; 8 9var Form = t.form.Form; 10 11// here we are: define your domain model 12var Person = t.struct({ 13 name: t.String, // a required string 14 surname: t.maybe(t.String), // an optional string 15 age: t.Number, // a required number 16 rememberMe: t.Boolean // a boolean 17}); 18 19var options = {}; // optional rendering options (see documentation) 20 21var AwesomeProject = React.createClass({ 22 23 onPress: function () { 24 // call getValue() to get the values of the form 25 var value = this.refs.form.getValue(); 26 if (value) { // if validation fails, value will be null 27 console.log(value); // value here is an instance of Person 28 } 29 }, 30 31 render: function() { 32 return ( 33 <View style={styles.container}> 34 {/* display */} 35 <Form 36 ref="form" 37 type={Person} 38 options={options} 39 /> 40 <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'> 41 <Text style={styles.buttonText}>Save</Text> 42 </TouchableHighlight> 43 </View> 44 ); 45 } 46}); 47 48var styles = StyleSheet.create({ 49 container: { 50 justifyContent: 'center', 51 marginTop: 50, 52 padding: 20, 53 backgroundColor: '#ffffff', 54 }, 55 buttonText: { 56 fontSize: 18, 57 color: 'white', 58 alignSelf: 'center' 59 }, 60 button: { 61 height: 36, 62 backgroundColor: '#48BBEC', 63 borderColor: '#48BBEC', 64 borderWidth: 1, 65 borderRadius: 8, 66 marginBottom: 10, 67 alignSelf: 'stretch', 68 justifyContent: 'center' 69 } 70}); 71 72AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);
Output:
(Labels are automatically generated)
Ouput after a validation error:
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).
The Form
component behaves like a controlled component:
1var Person = t.struct({ 2 name: t.String, 3 surname: t.maybe(t.String) 4}); 5 6var AwesomeProject = React.createClass({ 7 8 getInitialState() { 9 return { 10 value: { 11 name: 'Giulio', 12 surname: 'Canti' 13 } 14 }; 15 }, 16 17 onChange(value) { 18 this.setState({value}); 19 }, 20 21 onPress: function () { 22 var value = this.refs.form.getValue(); 23 if (value) { 24 console.log(value); 25 } 26 }, 27 28 render: function() { 29 return ( 30 <View style={styles.container}> 31 <Form 32 ref="form" 33 type={Person} 34 value={this.state.value} 35 onChange={this.onChange} 36 /> 37 <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'> 38 <Text style={styles.buttonText}>Save</Text> 39 </TouchableHighlight> 40 </View> 41 ); 42 } 43});
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 changeWarning. tcomb-form-native uses
shouldComponentUpdate
aggressively. In order to ensure that tcomb-form-native detect any change totype
,options
orvalue
props you have to change references:
1var Type = t.struct({ 2 disable: t.Boolean, // if true, name field will be disabled 3 name: t.String 4}); 5 6// see the "Rendering options" section in this guide 7var options = { 8 fields: { 9 name: {} 10 } 11}; 12 13var AwesomeProject = React.createClass({ 14 15 getInitialState() { 16 return { 17 options: options, 18 value: null 19 }; 20 }, 21 22 onChange(value) { 23 // tcomb immutability helpers 24 // https://github.com/gcanti/tcomb/blob/master/docs/API.md#updating-immutable-instances 25 var options = t.update(this.state.options, { 26 fields: { 27 name: { 28 editable: {'$set': !value.disable} 29 } 30 } 31 }); 32 this.setState({options: options, value: value}); 33 }, 34 35 onPress: function () { 36 var value = this.refs.form.getValue(); 37 if (value) { 38 console.log(value); 39 } 40 }, 41 42 render: function() { 43 return ( 44 <View style={styles.container}> 45 <Form 46 ref="form" 47 type={Type} 48 options={this.state.options} 49 value={this.state.value} 50 onChange={this.onChange} 51 /> 52 <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'> 53 <Text style={styles.buttonText}>Save</Text> 54 </TouchableHighlight> 55 </View> 56 ); 57 } 58 59});
You can get access to a field with the getComponent(path)
API:
1var Person = t.struct({ 2 name: t.String, 3 surname: t.maybe(t.String), 4 age: t.Number, 5 rememberMe: t.Boolean 6}); 7 8var AwesomeProject = React.createClass({ 9 10 componentDidMount() { 11 // give focus to the name textbox 12 this.refs.form.getComponent('name').refs.input.focus(); 13 }, 14 15 onPress: function () { 16 var value = this.refs.form.getValue(); 17 if (value) { 18 console.log(value); 19 } 20 }, 21 22 render: function() { 23 return ( 24 <View style={styles.container}> 25 <Form 26 ref="form" 27 type={Person} 28 /> 29 <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'> 30 <Text style={styles.buttonText}>Save</Text> 31 </TouchableHighlight> 32 </View> 33 ); 34 } 35});
1var Person = t.struct({ 2 name: t.String, 3 surname: t.maybe(t.String), 4 age: t.Number, 5 rememberMe: t.Boolean 6}); 7 8var AwesomeProject = React.createClass({ 9 10 getInitialState() { 11 return { value: null }; 12 }, 13 14 onChange(value) { 15 this.setState({ value }); 16 }, 17 18 clearForm() { 19 // clear content from all textbox 20 this.setState({ value: null }); 21 }, 22 23 onPress: function () { 24 var value = this.refs.form.getValue(); 25 if (value) { 26 console.log(value); 27 // clear all fields after submit 28 this.clearForm(); 29 } 30 }, 31 32 render: function() { 33 return ( 34 <View style={styles.container}> 35 <Form 36 ref="form" 37 type={Person} 38 value={this.state.value} 39 onChange={this.onChange.bind(this)} 40 /> 41 <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'> 42 <Text style={styles.buttonText}>Save</Text> 43 </TouchableHighlight> 44 </View> 45 ); 46 } 47});
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:
1const Country = t.enums({ 2 'IT': 'Italy', 3 'US': 'United States' 4}, 'Country'); 5 6var AwesomeProject = React.createClass({ 7 8 // returns the suitable type based on the form value 9 getType(value) { 10 if (value.country === 'IT') { 11 return t.struct({ 12 country: Country, 13 rememberMe: t.Boolean 14 }); 15 } else if (value.country === 'US') { 16 return t.struct({ 17 country: Country, 18 name: t.String 19 }); 20 } else { 21 return t.struct({ 22 country: Country 23 }); 24 } 25 }, 26 27 getInitialState() { 28 const value = {}; 29 return { value, type: this.getType(value) }; 30 }, 31 32 onChange(value) { 33 // recalculate the type only if strictly necessary 34 const type = value.country !== this.state.value.country ? 35 this.getType(value) : 36 this.state.type; 37 this.setState({ value, type }); 38 }, 39 40 onPress() { 41 var value = this.refs.form.getValue(); 42 if (value) { 43 console.log(value); 44 } 45 }, 46 47 render() { 48 49 return ( 50 <View style={styles.container}> 51 <t.form.Form 52 ref="form" 53 type={this.state.type} 54 value={this.state.value} 55 onChange={this.onChange} 56 /> 57 <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'> 58 <Text style={styles.buttonText}>Save</Text> 59 </TouchableHighlight> 60 </View> 61 ); 62 } 63});
By default fields are required:
1var Person = t.struct({ 2 name: t.String, // a required string 3 surname: t.String // a required string 4});
In order to create an optional field, wrap the field type with the t.maybe
combinator:
1var Person = t.struct({ 2 name: t.String, 3 surname: t.String, 4 email: t.maybe(t.String) // an optional string 5});
The postfix " (optional)"
is automatically added to optional fields.
You can customise the postfix value or setting a postfix for required fields:
1t.form.Form.i18n = { 2 optional: '', 3 required: ' (required)' // inverting the behaviour: adding a postfix to the required fields 4};
In order to create a numeric field, use the t.Number
type:
1var Person = t.struct({ 2 name: t.String, 3 surname: t.String, 4 email: t.maybe(t.String), 5 age: t.Number // a numeric field 6});
tcomb-form-native will convert automatically numbers to / from strings.
In order to create a boolean field, use the t.Boolean
type:
1var Person = t.struct({ 2 name: t.String, 3 surname: t.String, 4 email: t.maybe(t.String), 5 age: t.Number, 6 rememberMe: t.Boolean // a boolean field 7});
Booleans are displayed as SwitchIOS
s.
In order to create a date field, use the t.Date
type:
1var Person = t.struct({ 2 name: t.String, 3 surname: t.String, 4 email: t.maybe(t.String), 5 age: t.Number, 6 birthDate: t.Date // a date field 7});
Dates are displayed as DatePickerIOS
s 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:
1// see the "Rendering options" section in this guide 2var options = { 3 fields: { 4 birthDate: { 5 mode: 'date' // display the Date field as a DatePickerAndroid 6 } 7 } 8};
config
optionThe 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:
Key | Value |
---|---|
animation | The animation to collapse the date picker. Defaults to Animated.timing . |
animationConfig | The animation configuration object. Defaults to {duration: 200} for the default animation. |
format | A (date) => String(date) kind of function to provide a custom date format parsing to display the value. Optional, defaults to (date) => String(date) . |
defaultValueText | An 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.
config
optionWhen using a t.Date
type in Android, it can be configured through a config
option that take the following parameters:
Key | Value |
---|---|
background | Determines the type of background drawable that's going to be used to display feedback. Optional, defaults to TouchableNativeFeedback.SelectableBackground . |
format | A (date) => String(date) kind of function to provide a custom date format parsing to display the value. Optional, defaults to (date) => String(date) . |
dialogMode | Determines the type of datepicker mode for Android (default , spinner or calendar ). |
defaultValueText | An string to customize the default value of the null date value text. |
In order to create an enum field, use the t.enums
combinator:
1var Gender = t.enums({ 2 M: 'Male', 3 F: 'Female' 4}); 5 6var Person = t.struct({ 7 name: t.String, 8 surname: t.String, 9 email: t.maybe(t.String), 10 age: t.Number, 11 rememberMe: t.Boolean, 12 gender: Gender // enum 13});
Enums are displayed as Picker
s.
config
optionThe 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:
Key | Value |
---|---|
animation | The animation to collapse the date picker. Defaults to Animated.timing . |
animationConfig | The 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.
A predicate is a function with the following signature:
(x: any) => boolean
You can refine a type with the t.refinement(type, predicate)
combinator:
1// a type representing positive numbers 2var Positive = t.refinement(t.Number, function (n) { 3 return n >= 0; 4}); 5 6var Person = t.struct({ 7 name: t.String, 8 surname: t.String, 9 email: t.maybe(t.String), 10 age: Positive, // refinement 11 rememberMe: t.Boolean, 12 gender: Gender 13});
Subtypes allow you to express any custom validation with a simple predicate.
In order to customize the look and feel, use an options
prop:
1<Form type={Model} options={options} />
By default labels are automatically generated. You can turn off this behaviour or override the default labels on field basis.
1var options = { 2 label: 'My struct label' // <= form legend, displayed before the fields 3}; 4 5var options = { 6 fields: { 7 name: { 8 label: 'My name label' // <= label for the name field 9 } 10 } 11};
In order to automatically generate default placeholders, use the option auto: 'placeholders'
:
1var options = { 2 auto: 'placeholders' 3}; 4 5<Form type={Person} options={options} />
Set auto: 'none'
if you don't want neither labels nor placeholders.
1var options = { 2 auto: 'none' 3};
You can sort the fields with the order
option:
1var options = { 2 order: ['name', 'surname', 'rememberMe', 'gender', 'age', 'email'] 3};
You can set the default values passing a value
prop to the Form
component:
1var value = { 2 name: 'Giulio', 3 surname: 'Canti', 4 age: 41, 5 gender: 'M' 6}; 7 8<Form type={Model} value={value} />
You can configure each field with the fields
option:
1var options = { 2 fields: { 3 name: { 4 // name field configuration here.. 5 }, 6 surname: { 7 // surname field configuration here.. 8 } 9 } 10};
Implementation: TextInput
Tech note. Values containing only white spaces are converted to null
.
You can set the placeholder with the placeholder
option:
1var options = { 2 fields: { 3 name: { 4 placeholder: 'Your placeholder here' 5 } 6 } 7};
You can set the label with the label
option:
1var options = { 2 fields: { 3 name: { 4 label: 'Insert your name' 5 } 6 } 7};
You can set a help message with the help
option:
1var options = { 2 fields: { 3 name: { 4 help: 'Your help message here' 5 } 6 } 7};
You can add a custom error message with the error
option:
1var options = { 2 fields: { 3 email: { 4 // you can use strings or JSX 5 error: 'Insert a valid email' 6 } 7 } 8};
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 validatedcontext
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:
1var options = { 2 hasError: true, 3 error: <i>A custom error message</i> 4};
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 validatedcontext
is the value of the context
prop. Also it contains a reference to the component options.1var Age = t.refinement(t.Number, function (n) { return n >= 18; }); 2 3// if you define a getValidationErrorMessage function, it will be called on validation errors 4Age.getValidationErrorMessage = function (value, path, context) { 5 return 'bad age, locale: ' + context.locale; 6}; 7 8var Schema = t.struct({ 9 age: Age 10}); 11 12... 13 14<t.form.Form 15 ref="form" 16 type={Schema} 17 context={{locale: 'it-IT'}} 18/>
You can even define getValidationErrorMessage
on the supertype in order to be DRY:
1t.Number.getValidationErrorMessage = function (value, path, context) { 2 return 'bad number'; 3}; 4 5Age.getValidationErrorMessage = function (value, path, context) { 6 return 'bad age, locale: ' + context.locale; 7};
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
Implementation: SwitchIOS
The following options are similar to the Textbox
component's ones:
label
help
error
The following standard options are available (see http://facebook.github.io/react-native/docs/switchios.html):
disabled
onTintColor
thumbTintColor
tintColor
Implementation: PickerIOS
The following options are similar to the Textbox
component's ones:
label
help
error
nullOption
optionYou can customize the null option with the nullOption
option:
1var options = { 2 fields: { 3 gender: { 4 nullOption: {value: '', text: 'Choose your gender'} 5 } 6 } 7};
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
.
You can sort the options with the order
option:
1var options = { 2 fields: { 3 gender: { 4 order: 'asc' // or 'desc' 5 } 6 } 7};
You can determinate if Select is collapsed:
1var options = { 2 fields: { 3 gender: { 4 isCollapsed: false // default: true 5 } 6 } 7};
If option not set, default is true
You can set a callback, triggered, when collapse change:
1var options = { 2 fields: { 3 gender: { 4 onCollapseChange: () => { console.log('collapse changed'); } 5 } 6 } 7};
Implementation: DatePickerIOS
1var Person = t.struct({ 2 name: t.String, 3 birthDate: t.Date 4});
The following options are similar to the Textbox
component's ones:
label
help
error
The following standard options are available (see http://facebook.github.io/react-native/docs/datepickerios.html):
maximumDate
,minimumDate
,minuteInterval
,mode
,timeZoneOffsetInMinutes
For any component, you can set the field with the hidden
option:
1var options = { 2 fields: { 3 name: { 4 hidden: true 5 } 6 } 7};
This will completely skip the rendering of the component, while the default value will be available for validation purposes.
Code Example
1const AccountType = t.enums.of([ 2 'type 1', 3 'type 2', 4 'other' 5], 'AccountType') 6 7const KnownAccount = t.struct({ 8 type: AccountType 9}, 'KnownAccount') 10 11// UnknownAccount extends KnownAccount so it owns also the type field 12const UnknownAccount = KnownAccount.extend({ 13 label: t.String, 14}, 'UnknownAccount') 15 16// the union 17const Account = t.union([KnownAccount, UnknownAccount], 'Account') 18 19// the final form type 20const Type = t.list(Account) 21 22const options = { 23 item: [ // one options object for each concrete type of the union 24 { 25 label: 'KnownAccount' 26 }, 27 { 28 label: 'UnknownAccount' 29 } 30 ] 31}
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:
1// if account type is 'other' return the UnknownAccount type 2Account.dispatch = value => value && value.type === 'other' ? UnknownAccount : KnownAccount
You can handle a list with the t.list
combinator:
1const Person = t.struct({ 2 name: t.String, 3 tags: t.list(t.String) // a list of strings 4});
To configure all the items in a list, set the item
option:
1const Person = t.struct({ 2 name: t.String, 3 tags: t.list(t.String) // a list of strings 4}); 5 6const options = { 7 fields: { // <= Person options 8 tags: { 9 item: { // <= options applied to each item in the list 10 label: 'My tag' 11 } 12 } 13 } 14});
You can nest lists and structs at an arbitrary level:
1const Person = t.struct({ 2 name: t.String, 3 surname: t.String 4}); 5 6const 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:
1const Person = t.struct({ 2 name: t.Struct, 3 position: t.Struct({ 4 latitude: t.Number, 5 longitude: t.Number 6 }); 7}); 8 9const options = { 10 fields: { // <= Person options 11 name: { 12 label: 'name label' 13 } 14 position: { 15 fields: { 16 // Note that latitude is not directly nested in position, 17 // but in the fields property 18 latitude: { 19 label: 'My position label' 20 } 21 } 22 } 23 } 24});
When dealing with t.list
, make sure to declare the fields
property inside the item
property, as such:
1const Documents = t.struct({ 2 type: t.Number, 3 value: t.String 4}) 5 6const Person = t.struct({ 7 name: t.Struct, 8 documents: t.list(Documents) 9}); 10 11const options = { 12 fields: { 13 name: { /*...*/ }, 14 documents: { 15 item: { 16 fields: { 17 type: { 18 // Documents t.struct 'type' options 19 }, 20 value: { 21 // Documents t.struct 'value' options 22 } 23 } 24 } 25 } 26 } 27}
You can override the default language (english) with the i18n
option:
1const options = { 2 i18n: { 3 optional: ' (optional)', 4 required: '', 5 add: 'Add', // add button 6 remove: '✘', // remove button 7 up: '↑', // move up button 8 down: '↓' // move down button 9 } 10};
You can prevent operations on lists with the following options:
disableAdd
: (default false
) prevents adding new itemsdisableRemove
: (default false
) prevents removing existing itemsdisableOrder
: (default false
) prevents sorting existing items1const options = { 2 disableOrder: true 3};
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:
1const AccountType = t.enums.of([ 'type 1', 'type 2', 'other'], 'AccountType') 2 3const KnownAccount = t.struct({ 4 type: AccountType 5}, 'KnownAccount') 6 7// UnknownAccount extends KnownAccount so it owns also the type field 8const UnknownAccount = KnownAccount.extend({ 9 label: t.String, 10}, 'UnknownAccount') 11 12// the union 13const Account = t.union([KnownAccount, UnknownAccount], 'Account') 14 15// the final form type 16const 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:
1// if account type is 'other' return the UnknownAccount type 2Account.dispatch = value => value && value.type === 'other' ? UnknownAccount : KnownAccount
See also Stylesheet guide.
tcomb-form-native comes with a default style. You can customize the look and feel by setting another stylesheet:
1var t = require('tcomb-form-native/lib'); 2var i18n = require('tcomb-form-native/lib/i18n/en'); 3var templates = require('tcomb-form-native/lib/templates/bootstrap'); 4 5// define a stylesheet (see lib/stylesheets/bootstrap for an example) 6var stylesheet = {...}; 7 8// override globally the default stylesheet 9t.form.Form.stylesheet = stylesheet; 10// set defaults 11t.form.Form.templates = templates; 12t.form.Form.i18n = i18n;
You can also override the stylesheet locally for selected fields:
1var Person = t.struct({ 2 name: t.String 3}); 4 5var options = { 6 fields: { 7 name: { 8 stylesheet: myCustomStylesheet 9 } 10 } 11};
Or per form:
1var Person = t.struct({ 2 name: t.String 3}); 4 5var options = { 6 stylesheet: myCustomStylesheet 7};
For a complete example see the default stylesheet https://github.com/gcanti/tcomb-form-native/blob/master/lib/stylesheets/bootstrap.js.
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:
1var t = require('tcomb-form-native/lib'); 2var i18n = require('tcomb-form-native/lib/i18n/en'); 3var stylesheet = require('tcomb-form-native/lib/stylesheets/bootstrap'); 4 5// define the templates (see lib/templates/bootstrap for an example) 6var templates = {...}; 7 8// override globally the default layout 9t.form.Form.templates = templates; 10// set defaults 11t.form.Form.stylesheet = stylesheet; 12t.form.Form.i18n = i18n;
You can also override the template locally:
1var Person = t.struct({ 2 name: t.String 3}); 4 5function myCustomTemplate(locals) { 6 7 var containerStyle = {...}; 8 var labelStyle = {...}; 9 var textboxStyle = {...}; 10 11 return ( 12 <View style={containerStyle}> 13 <Text style={labelStyle}>{locals.label}</Text> 14 <TextInput style={textboxStyle} /> 15 </View> 16 ); 17} 18 19var options = { 20 fields: { 21 name: { 22 template: myCustomTemplate 23 } 24 } 25};
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:
1type Message = string | ReactElement 2 3{ 4 stylesheet: Object, // the styles to be applied 5 hasError: boolean, // true if there is a validation error 6 error: ?Message, // the optional error message to be displayed 7 label: Message, // the label to be displayed 8 help: ?Message, // the optional help message to be displayed 9 value: boolean, // the current value of the checkbox 10 onChange: Function, // the event handler to be called when the value changes 11 config: Object, // an optional object to pass configuration options to the new template 12 13 ...other input options here... 14 15}
For a complete example see the default template https://github.com/gcanti/tcomb-form-native/blob/master/lib/templates/bootstrap.
tcomb-form-native comes with a default internationalization (English). You can change it by setting another i18n object:
1var t = require('tcomb-form-native/lib'); 2var templates = require('tcomb-form-native/lib/templates/bootstrap'); 3 4// define an object containing your translations (see tcomb-form-native/lib/i18n/en for an example) 5var i18n = {...}; 6 7// override globally the default i18n 8t.form.Form.i18n = i18n; 9// set defaults 10t.form.Form.templates = templates; 11t.form.Form.stylesheet = stylesheet;
Say you want a search textbox which accepts a list of keywords separated by spaces:
1var Search = t.struct({ 2 search: t.list(t.String) 3});
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:
1var options = { 2 fields: { 3 search: { 4 factory: t.form.Textbox 5 } 6 } 7};
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:
1var Transformer = t.struct({ 2 format: t.Function, // from value to string, it must be idempotent 3 parse: t.Function // from string to value 4});
A basic transformer implementation for the search textbox:
1var listTransformer = { 2 format: function (value) { 3 return Array.isArray(value) ? value.join(' ') : value; 4 }, 5 parse: function (str) { 6 return str ? str.split(' ') : []; 7 } 8};
Now you can handle lists using the transformer option:
1// example of initial value 2var value = { 3 search: ['climbing', 'yosemite'] 4}; 5 6var options = { 7 fields: { 8 search: { 9 factory: t.form.Textbox, // tell tcomb-react-native to use the same component for textboxes 10 transformer: listTransformer, 11 help: 'Keywords are separated by spaces' 12 } 13 } 14};
You can pack together style, template (and transformers) in a custom component and then you can use it with the factory
option:
1var Component = t.form.Component; 2 3// extend the base Component 4class MyComponent extends Component { 5 6 // this is the only required method to implement 7 getTemplate() { 8 // define here your custom template 9 return function (locals) { 10 11 //return ... jsx ... 12 13 }; 14 } 15 16 // you can optionally override the default getLocals method 17 // it will provide the locals param to your template 18 getLocals() { 19 20 // in locals you'll find the default locals: 21 // - path 22 // - error 23 // - hasError 24 // - label 25 // - onChange 26 // - stylesheet 27 var locals = super.getLocals(); 28 29 // add here your custom locals 30 31 return locals; 32 } 33 34 35} 36 37// as example of transformer: this is the default transformer for textboxes 38MyComponent.transformer = { 39 format: value => Nil.is(value) ? null : value, 40 parse: value => (t.String.is(value) && value.trim() === '') || Nil.is(value) ? null : value 41}; 42 43var Person = t.struct({ 44 name: t.String 45}); 46 47var options = { 48 fields: { 49 name: { 50 factory: MyComponent 51 } 52 } 53};
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
},
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
Found 16/30 approved changesets -- score normalized to 5
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Reason
54 existing vulnerabilities detected
Details
Score
Last Scanned on 2025-07-07
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More