A React component that automatically generates interactive web forms from JSON Schema definitions.
—
The theme system provides a higher-order component approach for applying consistent UI themes and customizing form appearance across your application. It allows you to create reusable themed form components.
Creates a higher-order component that wraps the Form component with theme-specific field, widget, and template overrides.
/**
* Creates a higher-order component with theme overrides
* @param themeProps - Theme configuration with field, widget, and template overrides
* @returns Themed Form component that accepts standard FormProps
*/
function withTheme<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
themeProps: ThemeProps<T, S, F>
): ComponentType<FormProps<T, S, F>>;Usage Examples:
import { withTheme } from "@rjsf/core";
import validator from "@rjsf/validator-ajv8";
// Create a themed form component
const MaterialTheme = withTheme({
widgets: {
TextWidget: MaterialTextWidget,
SelectWidget: MaterialSelectWidget,
CheckboxWidget: MaterialCheckboxWidget,
},
templates: {
FieldTemplate: MaterialFieldTemplate,
ObjectFieldTemplate: MaterialObjectFieldTemplate,
}
});
// Use the themed form
const MyForm = () => (
<MaterialTheme
schema={schema}
validator={validator}
formData={formData}
onChange={handleChange}
/>
);Configuration interface for theme overrides that can be applied to forms.
interface ThemeProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> {
/** Override field components for different schema types */
fields?: RegistryFieldsType<T, S, F>;
/** Override template components for layout and presentation */
templates?: Partial<Omit<TemplatesType<T, S, F>, 'ButtonTemplates'>> & {
ButtonTemplates?: Partial<TemplatesType<T, S, F>['ButtonTemplates']>;
};
/** Override widget components for input controls */
widgets?: RegistryWidgetsType<T, S, F>;
/** Internal form wrapper component */
_internalFormWrapper?: ElementType;
}import { withTheme, getDefaultRegistry } from "@rjsf/core";
import type { ThemeProps } from "@rjsf/core";
// Get default registry to extend
const defaultRegistry = getDefaultRegistry();
// Create comprehensive theme
const customTheme: ThemeProps = {
widgets: {
...defaultRegistry.widgets,
TextWidget: CustomTextWidget,
SelectWidget: CustomSelectWidget,
DateWidget: CustomDateWidget,
},
templates: {
FieldTemplate: CustomFieldTemplate,
ErrorListTemplate: CustomErrorListTemplate,
ObjectFieldTemplate: CustomObjectFieldTemplate,
ButtonTemplates: {
SubmitButton: CustomSubmitButton,
AddButton: CustomAddButton,
RemoveButton: CustomRemoveButton,
}
},
fields: {
...defaultRegistry.fields,
StringField: CustomStringField,
}
};
const ThemedForm = withTheme(customTheme);
// Use with all standard Form props
<ThemedForm
schema={schema}
validator={validator}
formData={formData}
onChange={handleChange}
onSubmit={handleSubmit}
/>// Theme with just widget overrides
const WidgetTheme = withTheme({
widgets: {
TextWidget: BootstrapTextWidget,
SelectWidget: BootstrapSelectWidget,
}
});
// Theme with just template overrides
const TemplateTheme = withTheme({
templates: {
FieldTemplate: ({ children, classNames, description, errors, help, hidden, id, label, rawDescription, rawErrors, rawHelp, required, displayLabel }) => (
<div className={`custom-field ${classNames}`}>
{displayLabel && label && <label htmlFor={id}>{label}{required && " *"}</label>}
{children}
{description && <div className="field-description">{description}</div>}
{errors && <div className="field-errors">{errors}</div>}
</div>
)
}
});// Base theme
const baseTheme: ThemeProps = {
widgets: {
TextWidget: BaseTextWidget,
SelectWidget: BaseSelectWidget,
}
};
// Extended theme
const extendedTheme: ThemeProps = {
...baseTheme,
widgets: {
...baseTheme.widgets,
CheckboxWidget: CustomCheckboxWidget,
DateWidget: CustomDateWidget,
},
templates: {
FieldTemplate: CustomFieldTemplate,
}
};
const ExtendedThemedForm = withTheme(extendedTheme);const WrapperTheme = withTheme({
_internalFormWrapper: ({ children }) => (
<div className="custom-form-wrapper">
<header>Custom Form Header</header>
{children}
<footer>Custom Form Footer</footer>
</div>
),
widgets: {
TextWidget: StyledTextWidget,
}
});// Bootstrap 4 theme
const Bootstrap4Theme = withTheme({
widgets: {
TextWidget: (props) => (
<input
className="form-control"
type="text"
value={props.value || ""}
onChange={(e) => props.onChange(e.target.value)}
disabled={props.disabled}
readOnly={props.readonly}
/>
),
SelectWidget: (props) => (
<select
className="form-control"
value={props.value || ""}
onChange={(e) => props.onChange(e.target.value)}
disabled={props.disabled}
>
{props.options.enumOptions?.map(({ value, label }) => (
<option key={value} value={value}>{label}</option>
))}
</select>
),
},
templates: {
FieldTemplate: ({ label, children, errors, description, required }) => (
<div className="form-group">
{label && (
<label className="control-label">
{label}
{required && <span className="text-danger"> *</span>}
</label>
)}
{children}
{description && <small className="form-text text-muted">{description}</small>}
{errors && <div className="text-danger">{errors}</div>}
</div>
),
}
});const getThemeForUser = (userPrefs) => {
const themes = {
material: MaterialTheme,
bootstrap: BootstrapTheme,
custom: CustomTheme,
};
return withTheme(themes[userPrefs.theme] || themes.bootstrap);
};
const UserForm = ({ user }) => {
const ThemedForm = getThemeForUser(user.preferences);
return (
<ThemedForm
schema={schema}
validator={validator}
formData={formData}
/>
);
};Install with Tessl CLI
npx tessl i tessl/npm-rjsf--core