There are two ways to create custom fields with Tina:
component propertyA field Component is React component that accepts three props:
field: The field definition for the current field.input: The data and callbacks necessary to make an input.meta: Metadata about the field in the form. (e.g. dirty, valid)Checkout the react-final-form docs for a more detailed description of the input and meta props.
Checkout this blog for a step-by-step guide on creating a custom field component.
A field plugin is a JavaScript object with three properties:
name: A string used to identify the component. This is the name that is set in a field definition. This name must be unique; if multiple plugins are registered with the same name, only the last will be used.Component: The component that will used in the form. The exact nature of this component depends on which form builder is being used.validate: An optional function that will be used to validate the field's data.Interface
interface FieldPlugin {
  __type: 'field'
  name: string
  Component: React.FC<any>
  type?: string
  validate?(
    value: any,
    allValues: any,
    meta: any,
    field: Field
  ): string | object | undefined
  parse?: (value: any, name: string, field: Field) => any
  format?: (value: any, name: string, field: Field) => any
  defaultValue?: any
}Checkout this blog for a more in-depth look at creating custom field plugins.
The optional validate function can be utilized to configure field validation.
Arguments
value: The field's current valueallValues: The current state of the entire formmeta: The form metadata for this fieldfield: The field's configurationimport { MapPicker, validateMap } from 'cms-field-my-map-picker'
let cms = new CMS()
cms.fields.add({
  name: 'map',
  Component: MapPicker,
  validate: validateMap,
})Here is an example of a simple text field plugin. The Component renders the label, the input, and the errors for the field.
import { CMS } from '@tinacms/core'
let cms = new CMS()
cms.fields.add({
  name: 'text',
  Component({ input, meta, field }) {
    return (
      <div>
        <label htmlFor={input.name}>{field.label || field.name}</label>
        <div>{field.description}</div>
        <input type="email" {...input} />
        <div className="field-error">{meta.error}</div>
      </div>
    )
  },
  validate(email, allValues, meta, field) {
    let isValidEmail = /.*@.*\..*/.test(email)
    if (!isValidEmail) return 'Invalid email address'
  },
})If you want to style the custom field to fit in with the rest of the Tina sidebar, you'll need to use the CSS custom properties defined in @tinacms/styles.
Example
import styled from 'styled-components'
// Use the Tina CSS variables in your styled component
const Label = styled.h3`
  color: var(--tina-color-primary);
  font-size: var(--tina-font-size-3);
  font-weight: var(--tina-font-weight-bold);
  border-radius: var(--tina-radius-big);
  border: 1px solid var(--tina-color-primary-light);
  transition: color linear ease var(--tina-timing-medium);
  padding: var(--tina-padding-small);
`