Skip to main content
CMSquestions

What Is a Custom Input Component in a CMS?

AdvancedQuick Answer

TL;DR

A custom input component replaces the default field editor in a CMS studio with a bespoke UI — such as a colour picker, map selector, or AI-assisted text field. In Sanity, any field can have its default input replaced with a React component, giving teams full control over the editorial experience without changing the underlying data model.

Key Takeaways

  • Custom input components replace default CMS field editors with purpose-built UIs.
  • In Sanity, custom inputs are React components registered via the components.input property in the schema.
  • Common uses: colour pickers, icon selectors, AI writing assistants, map coordinates, or product lookups.
  • Custom inputs receive the current field value and a set function, making them easy to integrate.
  • This is a key differentiator of Sanity: the studio is fully programmable with React.

A custom input component is a React component that replaces the default field editor rendered by a CMS studio. Instead of accepting user input through a generic text box, number spinner, or dropdown, the field is driven by a purpose-built UI that is tailored to the specific data it manages.

Why Default Inputs Are Not Always Enough

Out-of-the-box CMS field editors are designed to be generic. A string field renders a text input; a number field renders a number input. This works well for simple data, but editorial teams often work with data that has richer semantics — a hex colour code, a geographic coordinate, a product SKU from an external catalogue, or a tone-of-voice setting. Forcing editors to type raw values into a plain text box is error-prone and slow.

Custom input components solve this by letting developers ship a UI that matches the mental model of the data. The underlying data model does not change — a colour is still stored as a hex string — but the editor sees a colour picker wheel instead of a raw text field.

How Custom Inputs Work in Sanity

Sanity Studio is built on React, which means every input you see in the studio is already a React component. The studio exposes a first-class API that lets you swap any of those components for your own. You register a custom input by adding a components.input property to a field definition in your schema:

The custom component receives a standardised set of props from the studio, including the current field value, an onChange handler, focus management utilities, and schema metadata. This contract is stable across Sanity versions, so custom inputs are straightforward to maintain.

The Component Contract

Every custom input component receives the following key props from Sanity Studio:

  • value — the current field value as stored in the document
  • onChange — a function to emit patch operations when the value changes
  • schemaType — the resolved schema type definition for the field
  • readOnly — a boolean indicating whether the field is in read-only mode
  • elementProps — DOM props (id, ref, onFocus, onBlur) for accessibility and focus management

Mutations are expressed as patch operations using Sanity's set() helper from sanity, which ensures the studio's real-time collaboration and undo/redo systems work correctly with your custom component.

Common Use Cases

Custom inputs are used wherever the default editor creates friction or risk of error:

  • Colour pickers — replace a hex string field with a visual colour wheel
  • Icon selectors — browse and pick from an icon library rather than typing an icon name
  • Map coordinate pickers — click a map to set latitude and longitude instead of entering raw numbers
  • AI writing assistants — embed a prompt interface that generates or refines text directly in the field
  • Product or SKU lookups — search an external commerce platform and store the selected product ID
  • Slug previews — show a live URL preview alongside the slug input

Scope: Field, Object, and Document Inputs

Custom inputs are not limited to primitive fields. You can replace the input for an entire object type or even a document type. This means you can build fully custom document editors — for example, a drag-and-drop page builder — while still storing data in Sanity's structured content model. The studio's component system is hierarchical: field → object → document, and each level can be overridden independently.

Suppose you are building a brand management tool in Sanity. Your schema has a brandColour field of type string that stores a hex value. By default, editors see a plain text input and must type #3B82F6 manually — error-prone and unintuitive. Here is how you replace it with a colour picker.

Step 1 — Create the React Component

tsx
// components/ColourPickerInput.tsx
import { set, unset } from 'sanity'
import type { StringInputProps } from 'sanity'

export function ColourPickerInput(props: StringInputProps) {
  const { value, onChange, elementProps } = props

  function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
    const newValue = event.currentTarget.value
    onChange(newValue ? set(newValue) : unset())
  }

  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
      <input
        {...elementProps}
        type="color"
        value={value ?? '#000000'}
        onChange={handleChange}
        style={{ width: 48, height: 48, cursor: 'pointer', border: 'none' }}
      />
      <span style={{ fontFamily: 'monospace', fontSize: 14 }}>
        {value ?? 'No colour selected'}
      </span>
    </div>
  )
}

Step 2 — Register the Component in the Schema

typescript
// schemas/brand.ts
import { ColourPickerInput } from '../components/ColourPickerInput'

export const brandSchema = {
  name: 'brand',
  type: 'document',
  title: 'Brand',
  fields: [
    {
      name: 'name',
      type: 'string',
      title: 'Brand Name',
    },
    {
      name: 'brandColour',
      type: 'string',
      title: 'Brand Colour',
      components: {
        input: ColourPickerInput,  // 👈 swap the default input
      },
    },
  ],
}

What Happens at Runtime

When an editor opens a Brand document, Sanity Studio renders ColourPickerInput instead of the default string input. The editor clicks the colour swatch, picks a colour from the browser's native colour picker, and the hex value is written to the document via the set() patch. The stored data is still a plain hex string — no schema change was required. Undo/redo, real-time collaboration, and validation all continue to work as normal.

Extending Further: Fetching External Data

Because the component is plain React, you can call external APIs inside it. A product lookup input might call your commerce platform's search API on keypress, display results in a dropdown, and write the selected product ID to the document — all without any changes to the Sanity data model or the content delivery API.

Misconception 1: Custom Inputs Change the Data Model

A custom input component is purely a studio-side concern. It changes how editors interact with a field, not what is stored in the document. A colour picker still writes a hex string; a map picker still writes a latitude/longitude object. The content delivery API, GROQ queries, and any downstream consumers are completely unaffected.

Misconception 2: You Need to Build Everything from Scratch

Sanity Studio exposes its own UI component library (@sanity/ui) which you can use freely inside custom inputs. This means you get consistent styling, dark mode support, and accessibility out of the box without writing custom CSS. You can also call props.renderDefault(props) to render the default input and wrap it with additional UI — you do not have to replace it entirely.

Misconception 3: Custom Inputs Break Real-Time Collaboration

As long as you use Sanity's set() and unset() patch helpers from the sanity package to emit changes via onChange, the studio's collaboration engine handles the rest. Conflicts, presence indicators, and undo/redo all work correctly. Problems only arise if you bypass onChange and mutate the document directly via the Sanity client — which is an anti-pattern.

Misconception 4: Custom Inputs Are Only for Advanced Teams

While custom inputs do require React knowledge, the API surface is intentionally small. A minimal working custom input is fewer than 20 lines of code. The Sanity community also maintains a rich ecosystem of open-source custom input plugins — colour pickers, icon selectors, and more — that can be installed and registered in minutes without writing any component code.

Misconception 5: Custom Inputs Are the Same as Custom Field Types

A custom input component changes the editor UI for an existing schema type. A custom field type (defined in the schema) changes the data structure itself. These are orthogonal concerns. You can have a custom field type with the default input, a built-in field type with a custom input, or both together. Conflating the two leads to over-engineering — often a new schema type is not needed when a custom input on an existing type is sufficient.