Forms


Forms in Tina are the main building blocks of your CMS. You will use Forms to:

  • Arrange the editing interface for your content
  • Expose your content to mutation through user edits
  • Process and persist the changes to your content

Use Form Helpers to Get Started Faster

This document explains how to set up forms in any React project. If you're using Gatsby or Next.js, we have helper packages that streamline this process for specific workflows:

The recommended way to create forms with Tina is to use the form hooks. These are explained in detail later on in this document, but let's start with a high-level overview of how form hooks are used.

When using form hooks, they should be called inside a Page component; that is, the component that takes your content and renders a page from it. In the following contrived example, we have a Page component that receives its content in the component's props, including a title and some markdownContent:

import * as React from React
import ReactMarkdown from 'react-markdown'

export function Page(props) {
    return (
        <main>
            <h1>{props.title}</h1>
            <ReactMarkdown source={props.markdownContent}>
        </main>
    )
}

Here's how we might call useForm to create a form that will be used to edit this content:

import * as React from React
import ReactMarkdown from 'react-markdown'
import { useForm, usePlugin } from 'tinacms'

export function Page(props) {
    const [modifiedValues, form] = useForm(formConfig) // formConfig omitted for brevity; we'll get to this later
    usePlugin(form)
    return (
        <main>
            <h1>{modifiedValues.title}</h1>
            <ReactMarkdown source={modifiedValues.markdownContent}>
        </main>
    )
}

useForm returns an object containing all of the form's values that will change as the content is updated in the form. By switching out our original props in the rendering code for this new object, our page will re-render as the content is changed, giving us a real-time preview of the content!

Creating Forms in React

The useForm hook let's you create a form, but it does not register it with the CMS. Here is how that hook works:

const [modifiedValues, form] = useForm(formConfig, watchedVars)

Hook Return Values

Like other React hooks, the form hooks enclose their return data in an array, expecting developers to assign these values via destructuring.

The first piece of data returned (modifiedValues in the above example) is an object containing all the data that is made editable by the form. As users edit data in the form, the values in this object change.

The second piece of data (form in the above example) is an form object that the hook created.

Form Configuration

The first argument that useForm receives (formConfig in the above example) is the object used to configure the form. Forms in Tina are built upon the Final Form library, and inherit all of Final Form's configuration options.

You can see the all of Final Form's form config options in the Form Config Documentation, but the following options will most commonly be used when creating a form:

keydescription
initialValuesAn object containing the initial state of the form.
onSubmitA function that runs when the form is saved.

In addition to Final Form's options, Tina's form hooks accept the following additional configuration options:

interface FormOptions<S> {
  id: any
  label: string
  fields: Field[]
  loadInitialValues?: () => Promise<S>
  reset?(): void
  onChange?(state): void
  actions?: any[]
  __type?: string
}
keydescription
idA unique identifier for the form. This should be derived from the content to distinguish it from other instances of the form.
labelA label for the form that will appear in the sidebar.
fieldsAn array of fields that will define the shape of the form and how content is edited.
loadInitialValuesOptional: A function to load the initial form state asynchronously. Return a promise that passes an object of form values when it resolves.
resetOptional: A function that runs when the form state is reset by the user.
actionsOptional: An array of custom actions that will be added to the form.
onChangeOptional: A function that runs when the form values are changed.
__typeOptional: Sets the Form's plugin type. Automatically set based on which form hook is used.

Now that we know how to configure a form, let's revisit the simplified example from the beginning of this document to demonstrate how we might configure this form:

import * as React from React
import ReactMarkdown from 'react-markdown'
import { useForm, usePlugin } from 'tinacms'

export function Page(props) {
  const [modifiedValues] = useForm({
    id: props.fileRelativePath,
    label: 'Edit Post',
    fields: [
      {
        name: 'title',
        label: 'Title',
        component: 'text',
      },
      {
        name: 'markdownContent',
        label: 'content',
        component: 'markdown',
      }
    ],
    initialValues: {
      title: props.title,
      markdownContent: props.markdownContent
    },
    onSubmit: (formData) => {
      // save the new form data
    },
  })

  usePlugin(form)

  return (
    <main>
      <h1>{modifiedValues.title}</h1>
      <ReactMarkdown source={modifiedValues.markdownContent}>
    </main>
  )
}

Note that when using these basic hooks, you are expected to implement the save functionality yourself by adding an onSubmit function. By default, Tina makes no assumptions about how your content is stored. These basic form hooks are building blocks for creating more purpose-built tools to fit specific use cases.

Watched Vars: Preserve Form Reactivity

The second argument that can be passed to the form hooks is an optional object containing values that the form will need to react to. Use this when dealing with data from external sources or in an environment with Hot Module Replacement; in other words, when you expect this data to change and it's essential to keep the form in sync with it.

interface WatchableFormValue {
  values: object
  label: string
  fields: Field[]
}
keydescription
valuesForm will update its values when this data changes, but will avoid updating a field if it has UI focus. This is useful for keeping values in sync with a remote content source.
fieldsBy watching the form's fields, they can be added/removed dynamically.
labelWhen the form's label is derived from a dynamic value, this will ensure it is updated correctly.

Form Helpers

The three hooks described thus far are the basic interface for creating forms, and they aim to support a broad set of use cases. For specific use cases, we've created some simplified interfaces for quickly setting up forms. Take a look at our Next.js form docs and Gatsby docs to learn how to use these.

Inline Forms

Refer to the Inline Editing docs.