A custom component can be passed and rendered by setting the ui.component property on a field.
This component completely overrides the original component, providing the user with the ability to fully customize the field.
ui.component
PropertyThis should be set to a valid 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
).form
: The CMS form, use to retrieve and update other fields from the same collection.To keep the same label, description, validation and other meta settings on your custom component, you can make use of the wrapper function wrapFieldsWithMeta
.
The form
prop can be used to control form values in other fields.
Any field in the collection can be updated with form.change
– form.change(name: string, value: any) => void
, using the fields name
property.
Any field in the collection can be accessed using form.getFieldState(name: string)?.value
, using the fields name
property.
Further usage patterns can be inferred from the react-final-form FormAPI docs.
Setting component.ui
to the value "hidden"
removes the field from the editor.
The field still exists behind the scenes and can be accessed in other ways.
Here is a custom slider component that can be used for adjusting image saturation.
{label: "Image Saturation",name: "saturation",type: "number",description: "My custom saturation field",ui: {parse: (val) => Number(val),component: wrapFieldsWithMeta(({ field, input, meta }) => {return (<div><inputname="saturation"id="saturation"type="range"min="0"max="10"step=".1"// Passes props.input.onChange{...input}/><br />Value: {input.value}</div>)})}}
NextJS and other frameworks are able to improve performance if image widths and heights are known ahead of time.
TinaCMS can be used to determine and store this on upload.
We can pass along the existing TinaCMS Image component, and intercept changes to it with a useEffect
that updates our width and height fields.
import { ImageField } from 'tinacms';const CustomImageField = (props) => {const loadImage = async (url) => {const img = new Image();img.src = url;await img.decode();return img;};const { form, input } = props;useEffect(() => {loadImage(input.value).then((img) => {form.change("imgWidth", img.naturalWidth);form.change("imgHeight", img.naturalHeight);});}, [form, input]);return ImageField(props);};
In the content model we also need two additional fields for height and width, which are set to "hidden"
so they don't appear in the editor.
{type: 'image',label: 'Hero image',name: 'imgSrc',ui: {component: CustomImageField,},},{type: "number",name: "imgWidth",ui: {component: "hidden",},},{type: "number",name: "imgHeight",ui: {component: "hidden",},}
This shows up as a singular Image input in the editor, but 3 values will be saved on each update (image source, height and width).
imgSrc: /uploads/llama - 2.avifimgWidth: 987imgHeight: 1480
Conditional fields can be created by checking values of other form fields.
With this approach, a hidden conditional field will hold whatever value was saved to it before it was hidden.
{type: "boolean",name: "imageEnabled",label: "Image Enabled",},{type: 'image',label: 'Hero image',name: 'imgSrc',ui: {component: (props) => {const { form } = props;return form.getFieldState("imageEnabled")?.value ?ImageField(props) : null;},},},
The below tutorial includes a segment on creating a custom Icon selector using a custom field component.
© TinaCMS 2019–2025