Skip to content
Endatix
Extensibility

Custom Question Types

Endatix Hub is designed to make SurveyJS easy to customize. Drop your custom code into the designated directory and your own widgets register automatically.

Custom Question Types Custom Expressions Custom Validation Rules
Question folder structure
customizations/questions/
color-picker/
index.ts required
color-picker.question-model.ts required
color-picker.component.tsx required
color-picker-icon.tsx optional
README.md optional
question-registry.ts auto-generated

Create upgrade-safe customizations.

Add your code to the designated customizations/questions/ folder. Run pnpm dev - Endatix Hub's discovery script finds it, updates the registry, and your SurveyJS customization is live.

The pattern follows the official SurveyJS custom question docs exactly. If you've built a custom question type for SurveyJS before, you already know the pattern.

What can you build?

Any React component can become a first-class question type. Here are a few ideas to spark inspiration.

Barcode & QR Scanner

Scan product codes, ticket IDs, and asset tags via the device camera. Connect @zxing/browser or html5-qrcode to eliminate manual entry entirely.

@zxing/browser

Store Locator

Embed an interactive map where respondents drop a pin or search for a nearby branch. Backed by react-map-gl, Leaflet, or the Google Maps JS API.

react-map-gl

Address Autocomplete

Resolve a typed string to a structured {street, city, zip} object. Pair with Google Places or Mapbox to eliminate address-formatting errors.

react-places-autocomplete

Example: Color Picker question type

A complete real-world example, based on the color picker sample code from the official SurveyJS documentation.

color-picker.question-model.ts Question model & SurveyJS registration
// color-picker.question-model.ts
import { ElementFactory, Question, Serializer, SvgRegistry } from "survey-core";
import { editorLocalization } from "survey-creator-core";

export const COLOR_PICKER_TYPE = "color-picker";

// 1. Define the question model
export class QuestionColorPickerModel extends Question {
  getType() {
    return COLOR_PICKER_TYPE;
  }

  get colorPickerType() {
    return this.getPropertyValue("colorPickerType");
  }
  set colorPickerType(val) {
    this.setPropertyValue("colorPickerType", val);
  }
}

// 2. Register the type with SurveyJS
ElementFactory.Instance.registerElement(
  COLOR_PICKER_TYPE,
  (name: string) => new QuestionColorPickerModel(name)
);

// 3. Add localization labels for the Creator
const locale = editorLocalization.getLocale("");
locale.qt[COLOR_PICKER_TYPE] = "Color Picker";
locale.pe.colorPickerType = "Color picker type";

// 4. Declare serializable properties
Serializer.addClass(
  COLOR_PICKER_TYPE,
  [
    {
      name: "colorPickerType",
      default: "Slider",
      choices: ["Slider", "Sketch", "Compact"],
      category: "general",
      visibleIndex: 2,
    },
  ],
  () => new QuestionColorPickerModel(""),
  "question"
);
color-picker.component.tsx React component & renderer registration
// color-picker.component.tsx
import React from "react";
import { ReactQuestionFactory, SurveyQuestionElementBase } from "survey-react-ui";
import { COLOR_PICKER_TYPE } from "./color-picker.question-model";
import { SliderPicker, SketchPicker, CompactPicker } from "react-color";

export class SurveyQuestionColorPicker extends SurveyQuestionElementBase {
  get question() { return this.questionBase; }

  handleColorChange = (data: { hex: string }) => {
    this.question.value = data.hex;
  };

  renderElement() {
    const type = this.question.colorPickerType;
    return (
      <div>
        {type === "Slider" && (
          <SliderPicker
            color={this.question.value}
            onChange={this.handleColorChange}
          />
        )}
        {type === "Sketch" && (
          <SketchPicker
            color={this.question.value}
            onChange={this.handleColorChange}
          />
        )}
        {type === "Compact" && (
          <CompactPicker
            color={this.question.value}
            onChange={this.handleColorChange}
          />
        )}
      </div>
    );
  }
}

// Register the React component for this question type
ReactQuestionFactory.Instance.registerQuestion(COLOR_PICKER_TYPE, (props) =>
  React.createElement(SurveyQuestionColorPicker, props)
);
index.ts Module entry point
// index.ts - module entry point
import { createCustomQuestion } from "@/lib/questions/question-factory";
import { COLOR_PICKER_TYPE, QuestionColorPickerModel } from "./color-picker.question-model";
import "./color-picker.component"; // side-effect: registers React component

const questionModule = createCustomQuestion({
  name: COLOR_PICKER_TYPE,
  title: "Color Picker",
  iconName: COLOR_PICKER_TYPE,
  model: QuestionColorPickerModel,
});

export default questionModule;

What the customization system supports

Any Third-Party Component

Wrap any React library - date pickers, signature pads, barcode scanners, maps, rich text editors - as a first-class SurveyJS question type.

Appears in Creator Toolbar

Once registered, your question type appears in the SurveyJS Creator drag-and-drop toolbar just like built-in question types.

Property Grid Integration

Custom properties defined with Serializer.addClass appear in the Creator's Property Grid, letting non-technical users configure the question.

Design Mode Support

Questions receive the isDesignMode flag from SurveyJS. Toggle pointer events or show a placeholder in design mode so the Creator stays usable.

Auto-Discovery

Drop a folder in customizations/questions/ - no manual registration in a central file. The Hub discovery script finds and wires it on pnpm dev.

TypeScript-First

Full TypeScript support throughout. The question model, component props, and SurveyJS interfaces are all typed. IDE autocomplete works end-to-end.

Endatix Hub

Any React component can be a question type.

Explore the example questions in the Hub repository and ship your first custom question in an afternoon.

Browse Hub on GitHub