or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

field-components.mdform-component.mdindex.mdregistry-system.mdtemplate-components.mdtheme-system.mdwidget-components.md

theme-system.mddocs/

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

```