CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-rjsf--core

A React component that automatically generates interactive web forms from JSON Schema definitions.

Pending
Overview
Eval results
Files

theme-system.mddocs/

Theme System

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.

Capabilities

withTheme Function

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}
  />
);

ThemeProps Interface

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;
}

Advanced Theme Patterns

Complete Theme Implementation

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}
/>

Partial Theme Overrides

// 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>
    )
  }
});

Multiple Theme Composition

// 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);

Theme with Form Wrapper

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,
  }
});

Theme Integration Patterns

UI Framework Integration

// 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>
    ),
  }
});

Dynamic Theme Selection

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

docs

field-components.md

form-component.md

index.md

registry-system.md

template-components.md

theme-system.md

widget-components.md

tile.json