Recipe: Wrapping the default input widget in Sanity CMS
This post shares a recipe for creating a Custom Input Widget in Sanity CMS that wraps the default input widget.
This approach is useful when you want to intercept onChange events and/or add html around the default input widget.
Consider this WrappedDefaultInput
custom input widget.
import React, { Fragment } from "react";
import { FormBuilderInput } from "part:@sanity/form-builder";
export default class WrappedDefaultInput extends React.Component {
// class property, removes need for constructor to set initial state
state = {
hasOnChangeBeenCalled: false
};
// class property with es6 arrow function binds 'this' for us.
customOnChangeHandler = patchEvent => {
console.log("Debug:", { patchEvent });
if (!this.state.hasOnChangeBeenCalled) {
this.setState({ hasOnChangeBeenCalled: true });
}
this.props.onChange(patchEvent);
};
render() {
const { type = {}, value } = this.props;
// remove inputComponent property to prevent infinite loop caused by
// FormBuilderInput resolving to WrappedDefaultInput again and again.
const { inputComponent, ...restOfType } = type;
const updatedProps = {
...this.props,
type: restOfType,
// add a custom onChange function that intercepts the FormBuilderInput's
// onChange call and logs it out.
onChange: this.customOnChangeHandler
};
return (
<Fragment>
<p>
Has onChange been called:
{this.state.hasOnChangeBeenCalled ? "true" : "false"}
</p>
<FormBuilderInput {...updatedProps} />
<p>Current value of this schema field: {JSON.stringify(value)} </p>
</Fragment>
);
}
}
Key things to note about this widget:
- It will work for any schema field.
- It takes the schema field configuration and removes the custom input widget declaration before passing the
updatedProps
to the defaultFormBuilderInput
. This avoids crashing the Sanity Studio with an infinite loop where theFormBuilderInput
would keep resolving to theWrappedDefaultInput
. - This approach enables us to add html or css around a complex default input without having to re-create that input.
- It makes it easier to debug and modify complex onChange events.
In the screenshot you can see that I’ve wrapped a non-trivial schema field object type that not only has an image field but also string type fields for attribution and caption. Located above and below the default input we find output from our custom input widget. To the right we log out Sanity patchEvents in the browser’s web developer console.
I got to think a lot about future-compatibility when I wrote this code. What I’m especially pleased with is how I pass all of Sanity’s props to the FormBuilderInput
(minus the custom input widget). This widget doesn’t need to enumerate all the properties it receives just make sure to pass them on, and it allows for props to change in the future. This is very much in keeping with the thoughts put forth in Maybe Not by Rich Hickey.
My blog is still built with Hugo, but it’s cool to experiment with Sanity since I build projects with it at Netlife.
If this post helps you out, do give me a shoutout on Twitter. :)