Material UI theme integration for react-jsonschema-form providing dual v4/v5 compatibility with Material Design components, widgets, fields, and themes for JSON schema-based forms.
—
React Context system for providing Material UI components to form elements, enabling version compatibility and dependency injection. The context system allows widgets and fields to access Material UI components without direct imports.
React Context that provides Material UI components to all child widgets and fields.
/**
* React Context for providing Material UI components to widgets and fields
* Can hold either Material UI v4 or v5 component references
*/
declare const MuiComponentContext: React.Context<MaterialUIContextProps | Mui5ContextProps | null>;Features:
Usage:
Hook for accessing Material UI components from the context within widgets and fields.
/**
* Hook to access Material UI components from context
* @returns MaterialUIContextProps | Mui5ContextProps
* @throws Error if no context provider is found
*/
function useMuiComponent(): MaterialUIContextProps | Mui5ContextProps;Features:
Usage:
import { useMuiComponent } from "@rjsf/material-ui";
function CustomWidget(props: WidgetProps) {
const { TextField, Button } = useMuiComponent();
return (
<TextField
value={props.value}
onChange={(e) => props.onChange(e.target.value)}
/>
);
}Interface defining Material UI v4 component structure:
interface MaterialUIContextProps {
Box: React.ComponentType<BoxProps>;
Button: React.ComponentType<ButtonProps>;
Checkbox: React.ComponentType<CheckboxProps>;
Divider: React.ComponentType<DividerProps>;
Grid: React.ComponentType<GridProps>;
FormControl: React.ComponentType<FormControlProps>;
FormControlLabel: React.ComponentType<FormControlLabelProps>;
FormGroup: React.ComponentType<FormGroupProps>;
FormHelperText: React.ComponentType<FormHelperTextProps>;
FormLabel: React.ComponentType<FormLabelProps>;
IconButton: React.ComponentType<IconButtonProps>;
Input: React.ComponentType<InputProps>;
InputLabel: React.ComponentType<InputLabelProps>;
List: React.ComponentType<ListProps>;
ListItem: React.ComponentType<ListItemProps>;
ListItemIcon: React.ComponentType<ListItemIconProps>;
ListItemText: React.ComponentType<ListItemTextProps>;
MenuItem: React.ComponentType<MenuItemProps>;
Paper: React.ComponentType<PaperProps>;
Radio: React.ComponentType<RadioProps>;
RadioGroup: React.ComponentType<RadioGroupProps>;
Slider: React.ComponentType<SliderProps>;
TextField: React.ComponentType<Omit<TextFieldProps, 'color' | 'variant'>>;
Typography: React.ComponentType<TypographyProps>;
AddIcon: React.ComponentType<SvgIconProps>;
ArrowDownwardIcon: React.ComponentType<SvgIconProps>;
ArrowUpwardIcon: React.ComponentType<SvgIconProps>;
ErrorIcon: React.ComponentType<SvgIconProps>;
RemoveIcon: React.ComponentType<SvgIconProps>;
}Interface defining Material UI v5 component structure:
interface Mui5ContextProps {
Box: React.ComponentType<BoxProps>;
Button: React.ComponentType<ButtonProps>;
Checkbox: React.ComponentType<CheckboxProps>;
Divider: React.ComponentType<DividerProps>;
Grid: React.ComponentType<GridProps>;
FormControl: React.ComponentType<FormControlProps>;
FormControlLabel: React.ComponentType<FormControlLabelProps>;
FormGroup: React.ComponentType<FormGroupProps>;
FormHelperText: React.ComponentType<FormHelperTextProps>;
FormLabel: React.ComponentType<FormLabelProps>;
IconButton: React.ComponentType<IconButtonProps>;
Input: React.ComponentType<OutlinedInputProps>; // Different from v4
InputLabel: React.ComponentType<InputLabelProps>;
List: React.ComponentType<ListProps>;
ListItem: React.ComponentType<ListItemProps>;
ListItemIcon: React.ComponentType<ListItemIconProps>;
ListItemText: React.ComponentType<ListItemTextProps>;
MenuItem: React.ComponentType<MenuItemProps>;
Paper: React.ComponentType<PaperProps>;
Radio: React.ComponentType<RadioProps>;
RadioGroup: React.ComponentType<RadioGroupProps>;
Slider: React.ComponentType<SliderProps>;
TextField: React.ComponentType<Omit<TextFieldProps, 'color' | 'variant'>>;
Typography: React.ComponentType<TypographyProps>;
AddIcon: React.ComponentType<SvgIconProps>;
ArrowDownwardIcon: React.ComponentType<SvgIconProps>;
ArrowUpwardIcon: React.ComponentType<SvgIconProps>;
ErrorIcon: React.ComponentType<SvgIconProps>;
RemoveIcon: React.ComponentType<SvgIconProps>;
}Context value object containing Material UI v4 component references:
/**
* Object containing @material-ui/core v4 component references
* Null if Material UI v4 dependencies are not available
*/
declare const MaterialUIContext: MaterialUIContextProps | null;Context value object containing Material UI v5 component references:
/**
* Object containing @mui/material v5 component references
* Null if Material UI v5 dependencies are not available
*/
declare const Mui5Context: Mui5ContextProps | null;import { useMuiComponent } from "@rjsf/material-ui";
import { WidgetProps } from "@rjsf/core";
const CustomTextWidget: React.FC<WidgetProps> = (props) => {
const { TextField, FormControl, FormHelperText } = useMuiComponent();
return (
<FormControl fullWidth>
<TextField
id={props.id}
label={props.label}
value={props.value || ""}
onChange={(event) => props.onChange(event.target.value)}
onBlur={() => props.onBlur && props.onBlur(props.id, props.value)}
onFocus={() => props.onFocus && props.onFocus(props.id, props.value)}
disabled={props.disabled}
readonly={props.readonly}
required={props.required}
error={props.rawErrors && props.rawErrors.length > 0}
/>
{props.rawErrors && props.rawErrors.length > 0 && (
<FormHelperText error>
{props.rawErrors.join(", ")}
</FormHelperText>
)}
</FormControl>
);
};import { useMuiComponent } from "@rjsf/material-ui";
const VersionAwareComponent: React.FC = () => {
const components = useMuiComponent();
// Detect version by checking component differences
const isV5 = 'Input' in components &&
components.Input.toString().includes('OutlinedInput');
return (
<div>
<p>Using Material UI {isV5 ? 'v5' : 'v4'}</p>
<components.Button variant="contained">
Click me
</components.Button>
</div>
);
};import { MuiComponentContext } from "@rjsf/material-ui";
import * as MuiV5 from "@mui/material";
import * as MuiIcons from "@mui/icons-material";
const customContextValue: Mui5ContextProps = {
...MuiV5,
AddIcon: MuiIcons.Add,
ArrowDownwardIcon: MuiIcons.ArrowDownward,
ArrowUpwardIcon: MuiIcons.ArrowUpward,
ErrorIcon: MuiIcons.Error,
RemoveIcon: MuiIcons.Remove,
};
function CustomContextProvider({ children }: { children: React.ReactNode }) {
return (
<MuiComponentContext.Provider value={customContextValue}>
{children}
</MuiComponentContext.Provider>
);
}import { useMuiComponent } from "@rjsf/material-ui";
const SafeWidget: React.FC<WidgetProps> = (props) => {
try {
const { TextField } = useMuiComponent();
return (
<TextField
value={props.value}
onChange={(e) => props.onChange(e.target.value)}
/>
);
} catch (error) {
return (
<div style={{ color: 'red' }}>
Material UI components not available: {error.message}
</div>
);
}
};import { render } from "@testing-library/react";
import { MuiComponentContext } from "@rjsf/material-ui";
const mockContext: Partial<MaterialUIContextProps> = {
TextField: ({ value, onChange }) => (
<input value={value} onChange={(e) => onChange(e)} />
),
Button: ({ children, onClick }) => (
<button onClick={onClick}>{children}</button>
),
};
function renderWithMockContext(component: React.ReactElement) {
return render(
<MuiComponentContext.Provider value={mockContext as any}>
{component}
</MuiComponentContext.Provider>
);
}
// Usage in tests
test("widget renders correctly", () => {
const { getByRole } = renderWithMockContext(<CustomWidget {...props} />);
expect(getByRole("textbox")).toBeInTheDocument();
});The context system provides several key benefits:
The context system handles several error conditions:
The form wrappers automatically display user-friendly warnings when Material UI dependencies are not available, preventing runtime crashes and providing clear guidance for resolution.
Install with Tessl CLI
npx tessl i tessl/npm-rjsf--material-ui