0
# Theme System
1
2
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.
3
4
## Capabilities
5
6
### withTheme Function
7
8
Creates a higher-order component that wraps the Form component with theme-specific field, widget, and template overrides.
9
10
```typescript { .api }
11
/**
12
* Creates a higher-order component with theme overrides
13
* @param themeProps - Theme configuration with field, widget, and template overrides
14
* @returns Themed Form component that accepts standard FormProps
15
*/
16
function withTheme<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
17
themeProps: ThemeProps<T, S, F>
18
): ComponentType<FormProps<T, S, F>>;
19
```
20
21
**Usage Examples:**
22
23
```typescript
24
import { withTheme } from "@rjsf/core";
25
import validator from "@rjsf/validator-ajv8";
26
27
// Create a themed form component
28
const MaterialTheme = withTheme({
29
widgets: {
30
TextWidget: MaterialTextWidget,
31
SelectWidget: MaterialSelectWidget,
32
CheckboxWidget: MaterialCheckboxWidget,
33
},
34
templates: {
35
FieldTemplate: MaterialFieldTemplate,
36
ObjectFieldTemplate: MaterialObjectFieldTemplate,
37
}
38
});
39
40
// Use the themed form
41
const MyForm = () => (
42
<MaterialTheme
43
schema={schema}
44
validator={validator}
45
formData={formData}
46
onChange={handleChange}
47
/>
48
);
49
```
50
51
### ThemeProps Interface
52
53
Configuration interface for theme overrides that can be applied to forms.
54
55
```typescript { .api }
56
interface ThemeProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> {
57
/** Override field components for different schema types */
58
fields?: RegistryFieldsType<T, S, F>;
59
/** Override template components for layout and presentation */
60
templates?: Partial<Omit<TemplatesType<T, S, F>, 'ButtonTemplates'>> & {
61
ButtonTemplates?: Partial<TemplatesType<T, S, F>['ButtonTemplates']>;
62
};
63
/** Override widget components for input controls */
64
widgets?: RegistryWidgetsType<T, S, F>;
65
/** Internal form wrapper component */
66
_internalFormWrapper?: ElementType;
67
}
68
```
69
70
## Advanced Theme Patterns
71
72
### Complete Theme Implementation
73
74
```typescript
75
import { withTheme, getDefaultRegistry } from "@rjsf/core";
76
import type { ThemeProps } from "@rjsf/core";
77
78
// Get default registry to extend
79
const defaultRegistry = getDefaultRegistry();
80
81
// Create comprehensive theme
82
const customTheme: ThemeProps = {
83
widgets: {
84
...defaultRegistry.widgets,
85
TextWidget: CustomTextWidget,
86
SelectWidget: CustomSelectWidget,
87
DateWidget: CustomDateWidget,
88
},
89
templates: {
90
FieldTemplate: CustomFieldTemplate,
91
ErrorListTemplate: CustomErrorListTemplate,
92
ObjectFieldTemplate: CustomObjectFieldTemplate,
93
ButtonTemplates: {
94
SubmitButton: CustomSubmitButton,
95
AddButton: CustomAddButton,
96
RemoveButton: CustomRemoveButton,
97
}
98
},
99
fields: {
100
...defaultRegistry.fields,
101
StringField: CustomStringField,
102
}
103
};
104
105
const ThemedForm = withTheme(customTheme);
106
107
// Use with all standard Form props
108
<ThemedForm
109
schema={schema}
110
validator={validator}
111
formData={formData}
112
onChange={handleChange}
113
onSubmit={handleSubmit}
114
/>
115
```
116
117
### Partial Theme Overrides
118
119
```typescript
120
// Theme with just widget overrides
121
const WidgetTheme = withTheme({
122
widgets: {
123
TextWidget: BootstrapTextWidget,
124
SelectWidget: BootstrapSelectWidget,
125
}
126
});
127
128
// Theme with just template overrides
129
const TemplateTheme = withTheme({
130
templates: {
131
FieldTemplate: ({ children, classNames, description, errors, help, hidden, id, label, rawDescription, rawErrors, rawHelp, required, displayLabel }) => (
132
<div className={`custom-field ${classNames}`}>
133
{displayLabel && label && <label htmlFor={id}>{label}{required && " *"}</label>}
134
{children}
135
{description && <div className="field-description">{description}</div>}
136
{errors && <div className="field-errors">{errors}</div>}
137
</div>
138
)
139
}
140
});
141
```
142
143
### Multiple Theme Composition
144
145
```typescript
146
// Base theme
147
const baseTheme: ThemeProps = {
148
widgets: {
149
TextWidget: BaseTextWidget,
150
SelectWidget: BaseSelectWidget,
151
}
152
};
153
154
// Extended theme
155
const extendedTheme: ThemeProps = {
156
...baseTheme,
157
widgets: {
158
...baseTheme.widgets,
159
CheckboxWidget: CustomCheckboxWidget,
160
DateWidget: CustomDateWidget,
161
},
162
templates: {
163
FieldTemplate: CustomFieldTemplate,
164
}
165
};
166
167
const ExtendedThemedForm = withTheme(extendedTheme);
168
```
169
170
### Theme with Form Wrapper
171
172
```typescript
173
const WrapperTheme = withTheme({
174
_internalFormWrapper: ({ children }) => (
175
<div className="custom-form-wrapper">
176
<header>Custom Form Header</header>
177
{children}
178
<footer>Custom Form Footer</footer>
179
</div>
180
),
181
widgets: {
182
TextWidget: StyledTextWidget,
183
}
184
});
185
```
186
187
## Theme Integration Patterns
188
189
### UI Framework Integration
190
191
```typescript
192
// Bootstrap 4 theme
193
const Bootstrap4Theme = withTheme({
194
widgets: {
195
TextWidget: (props) => (
196
<input
197
className="form-control"
198
type="text"
199
value={props.value || ""}
200
onChange={(e) => props.onChange(e.target.value)}
201
disabled={props.disabled}
202
readOnly={props.readonly}
203
/>
204
),
205
SelectWidget: (props) => (
206
<select
207
className="form-control"
208
value={props.value || ""}
209
onChange={(e) => props.onChange(e.target.value)}
210
disabled={props.disabled}
211
>
212
{props.options.enumOptions?.map(({ value, label }) => (
213
<option key={value} value={value}>{label}</option>
214
))}
215
</select>
216
),
217
},
218
templates: {
219
FieldTemplate: ({ label, children, errors, description, required }) => (
220
<div className="form-group">
221
{label && (
222
<label className="control-label">
223
{label}
224
{required && <span className="text-danger"> *</span>}
225
</label>
226
)}
227
{children}
228
{description && <small className="form-text text-muted">{description}</small>}
229
{errors && <div className="text-danger">{errors}</div>}
230
</div>
231
),
232
}
233
});
234
```
235
236
### Dynamic Theme Selection
237
238
```typescript
239
const getThemeForUser = (userPrefs) => {
240
const themes = {
241
material: MaterialTheme,
242
bootstrap: BootstrapTheme,
243
custom: CustomTheme,
244
};
245
246
return withTheme(themes[userPrefs.theme] || themes.bootstrap);
247
};
248
249
const UserForm = ({ user }) => {
250
const ThemedForm = getThemeForUser(user.preferences);
251
252
return (
253
<ThemedForm
254
schema={schema}
255
validator={validator}
256
formData={formData}
257
/>
258
);
259
};
260
```