SurveyJS: Advanced Survey/Feedback/Quiz Library in JavaScript
| File Size: | 4.49 MB |
|---|---|
| Views Total: | 15493 |
| Last Update: | |
| Publish Date: | |
| Official Website: | Go to website |
| License: | MIT |
SurveyJS Form Library is a JavaScript survey and quiz library that renders JSON-based forms, questionnaires, NPS flows, scored quizzes, and data-entry screens in browser projects.
The library uses a split package model: survey-core handles the form model and survey-js-ui renders it for HTML, CSS, and JavaScript pages. A jQuery page can still mount SurveyJS through the jQuery wrapper created by the browser renderer.
Use the stable 2.5.30 packages for production pages. The 3.0 line exists on npm as 3.0.0-beta.6, and npm marks 2.5.30 as the latest stable release. Pin beta packages only for migration checks or v3 compatibility work.
Features:
- Render surveys and forms from a JSON schema.
- Build quizzes with local scoring rules.
- Validate required answers and input formats locally.
- Route respondents through conditional question logic.
- Collect submitted answers as JSON data.
- Load choice lists from remote endpoints.
- Apply dark, light, compact, and custom themes.
- Support plain JavaScript, jQuery pages, React, Angular, and Vue 3.
- Handle multi-page forms, dynamic panels, matrix questions, ratings, and comments.
Use Cases:
- A product team needs an NPS survey with different follow-up questions for promoters and detractors.
- An education site needs a quiz form that calculates a score after submission.
- A SaaS admin page needs a long onboarding form with page progress and required field checks.
- A service quote page needs repeatable project sections and JSON output for a CRM endpoint.
Download Extensions:
- survey-jquery
- survey-js-ui
- surveyjs-editor
- survey-creator-core
- survey-creator
- survey-creator-js
- survey-analytics
- surveyjs-widgets
- survey-pdf
- survey-react
- survey-react-ui
- survey-creator-react
- survey-vue
- survey-vue-ui
- survey-creator-vue
- survey-vue3-ui
- survey-angular
- survey-angular-ui
- survey-creator-angular
- survey-knockout
- survey-knockout-ui
- survey-creator-knockout
Table of Contents
- Install And Include SurveyJS Files
- Framework Packages
- Theme Setup
- Add The Target Container
- Basic Usage
- Configuration Options
- API Methods
- Events
- Advanced Examples
- Implementation Tips
- Advanced Extension Points
- Alternatives
- FAQs
- Changelog
How To Use It
Install And Include SurveyJS Files
For a browser-first page, load the CSS file, the core model, and the Vanilla JS renderer in this order. Add jQuery before SurveyJS only if you plan to call the jQuery wrapper.
<link href="https://unpkg.com/[email protected]/survey-core.min.css" rel="stylesheet"> <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <script src="https://unpkg.com/[email protected]/survey.core.min.js"></script> <script src="https://unpkg.com/[email protected]/themes/contrast-dark.min.js"></script> <script src="https://unpkg.com/[email protected]/survey-js-ui.min.js"></script>
Use npm for module-based apps or beta migration work.
npm install [email protected] [email protected] npm install [email protected] [email protected]
The current setup replaces old snippets that used survey.jquery.js and Survey.StylesManager. The modern script-tag path uses survey.core.min.js, survey-js-ui.min.js, new Survey.Model(), and optional applyTheme().
Framework Packages
SurveyJS uses the same JSON model across renderers. Pick the renderer package that matches the front-end stack.
survey-js-ui: Renders SurveyJS forms in plain HTML, CSS, and JavaScript pages.- jQuery pages: Use
survey-js-uiand call the generatedSurvey()wrapper after jQuery loads. survey-react-ui: Renders the same model with<Survey model={survey} />in React apps.survey-vue3-ui: Renders the same model with<SurveyComponent :model="survey" />in Vue 3 apps.survey-angular-ui: Renders the same model with<survey [model]="surveyModel"></survey>in Angular apps.
React, Vue, and Angular projects usually import survey-core/survey-core.css in the component that renders the form. Browser pages load the same stylesheet through a <link> tag. React apps that use server rendering should render SurveyJS on the client side.
Theme Setup
SurveyJS themes use CSS variables and theme objects. The default stylesheet applies the Default theme. Load a predefined theme script after survey.core.min.js in browser pages, then pass the theme object to applyTheme().
<link href="https://unpkg.com/[email protected]/survey-core.min.css" rel="stylesheet"> <script src="https://unpkg.com/[email protected]/survey.core.min.js"></script> <script src="https://unpkg.com/[email protected]/themes/layered-dark-panelless.min.js"></script> <script src="https://unpkg.com/[email protected]/survey-js-ui.min.js"></script>
const survey = new Survey.Model(surveyJson);
survey.applyTheme(SurveyTheme.LayeredDarkPanelless);
survey.render(document.getElementById("customerSurvey"));
Module-based apps can import theme objects from survey-core/themes.
import { Model } from "survey-core";
import { ContrastDark } from "survey-core/themes";
import "survey-core/survey-core.css";
const survey = new Model(surveyJson);
survey.applyTheme(ContrastDark);
A custom theme uses the same method. Pass a theme object with themeName, colorPalette, isPanelless, and cssVariables.
Add The Target Container
SurveyJS renders into a normal DOM element. Keep the container ID unique on the page.
<div id="customerSurvey"></div> <pre id="surveyOutput"></pre>
Basic Usage
This is the smallest working SurveyJS example for a browser page. It creates a form model from JSON, mounts it into the page, and prints the submitted data.
const surveyJson = {
title: "Customer Feedback",
showQuestionNumbers: "off",
elements: [
{
type: "rating",
name: "satisfaction",
title: "How satisfied are you with the new checkout flow?",
isRequired: true,
rateMin: 1,
rateMax: 5
},
{
type: "comment",
name: "notes",
title: "What should we adjust next?"
}
]
};
const survey = new Survey.Model(surveyJson);
survey.onComplete.add(function(sender) {
document.getElementById("surveyOutput").textContent =
JSON.stringify(sender.data, null, 2);
});
survey.render(document.getElementById("customerSurvey"));
The setup flow is direct. The JSON object defines the form. new Survey.Model() creates the model. onComplete reads submitted data. render() mounts the form into the target element.
If your page already uses jQuery, call the wrapper after you create the same model. Use this version for the mount step.
$(function() {
$("#customerSurvey").Survey({
model: survey
});
});
Configuration Options
SurveyJS uses a large JSON schema with survey-level fields, page fields, panel fields, and question fields. The official model contains many more properties. The fields below cover the main setup paths developers need for surveys, quizzes, dynamic forms, and stored JSON results.
title(String): Sets the visible form title.description(String): Adds helper text under the title.pages(Array): Defines a multi-page survey.elements(Array): Defines questions on a single page.type(String): Selects a question type such astext,comment,radiogroup,checkbox,dropdown,tagbox,rating,ranking,boolean,html,image,imagepicker,file,signaturepad,matrix,matrixdropdown,matrixdynamic,multipletext, orpaneldynamic.name(String): Sets the answer key in the submitted JSON.titleLocation(String): Moves an individual question title.descriptionLocation(String): Moves an individual question description.startWithNewLine(Boolean): Places a question on a new row.visible(Boolean): Sets initial visibility for a question, panel, or page.isRequired(Boolean): Requires a response before completion or page navigation.visibleIf(String): Shows a question only after an expression evaluates to true.enableIf(String): Locks or unlocks a question through an expression.requiredIf(String): Applies required validation through an expression.choices(Array): Sets local options for select-style questions.choicesVisibleIf(String): Filters visible choices through an expression.hasOther(Boolean): Adds an Other choice for select-style questions.showNoneItem(Boolean): Adds a None choice.showSelectAllItem(Boolean): Adds a Select All choice for multi-select questions.choicesByUrl(Object): Loads options from a remote endpoint.validators(Array): Adds checks such as email, numeric range, text length, regular expression, and answer count.defaultValue(Any): Prefills a question value.valueName(String): Stores an answer under a different result key.inputType(String): Sets the native input type for text questions.maskType(String): Applies an input mask for supported text input formats.rateMin(Number): Sets the first rating value.rateMax(Number): Sets the last rating value.rows(Array or Number): Defines matrix row items or comment field height.columns(Array): Defines matrix columns.cellType(String): Sets the question type for matrix dropdown cells.templateElements(Array): Defines the repeated fields inside a dynamic panel.panelCount(Number): Sets the starting number of dynamic panels.minPanelCount(Number): Sets the minimum number of dynamic panels.maxPanelCount(Number): Sets the maximum number of dynamic panels.calculatedValues(Array): Adds calculated values from expressions.triggers(Array): Runs actions after expression rules match.showQuestionNumbers(String or Boolean): Controls question numbering.showProgressBar(String or Boolean): Shows page or question progress.progressBarType(String): Chooses progress based on pages, questions, or required questions.firstPageIsStarted(Boolean): Uses the first page as a start screen.showPrevButton(Boolean): Shows or hides the Previous button.pageNextText(String): Changes the Next button label.pagePrevText(String): Changes the Previous button label.checkErrorsMode(String): Sets validation timing, such asonCompleteoronValueChanged.completeText(String): Changes the final button label.completedHtml(String): Shows custom HTML after submission.completedHtmlOnCondition(Array): Shows different completion HTML from expression rules.showPreviewBeforeComplete(Boolean or String): Shows an answer preview before final submission.navigateToUrl(String): Opens a URL after completion.sendResultOnPageNext(Boolean): Sends partial results after page navigation.clearInvisibleValues(String or Boolean): Controls how hidden question values stay in survey data.focusFirstQuestionAutomatic(Boolean): Moves focus to the first available question.widthMode(String): Sets responsive or fixed width behavior.fitToContainer(Boolean): Fits the survey layout to the container.locale(String): Selects a language after you load translation files.
API Methods
// Create a model from survey JSON.
const survey = new Survey.Model(surveyJson);
// Load a new JSON schema into the same model.
survey.fromJSON(nextSurveyJson);
// Export the current survey definition.
const savedSchema = survey.toJSON();
// Render the form inside a DOM element.
survey.render(document.getElementById("customerSurvey"));
// Start a survey that uses a start page.
survey.start();
// Apply a theme object loaded from survey-core themes.
survey.applyTheme(SurveyTheme.ContrastDark);
// Move to the next page after validation.
survey.nextPage();
// Move back one page.
survey.prevPage();
// Complete the form after normal validation.
survey.tryComplete();
// Complete the last page and submit current data.
survey.completeLastPage();
// Complete the form from code.
survey.doComplete();
// Return from preview mode to editing mode.
survey.cancelPreview();
// Set a question value from application code.
survey.setValue("plan", "team");
// Read one answer value.
const plan = survey.getValue("plan");
// Clear one answer value.
survey.clearValue("plan");
// Replace all current answers.
survey.data = { plan: "team", seats: 12 };
// Merge new answers into current data.
survey.mergeData({ seats: 20 });
// Read submitted answers in a flat display-friendly structure.
const plainData = survey.getPlainData();
// Store a value for expressions or app logic.
survey.setVariable("accountTier", "enterprise");
// Read a stored variable.
const tier = survey.getVariable("accountTier");
// Validate the current data.
const valid = survey.validate();
// Find one question by its JSON name.
const emailQuestion = survey.getQuestionByName("email");
// Read all questions from the form.
const questions = survey.getAllQuestions();
// Add a page at runtime.
const page = survey.addNewPage("followUp");
// Remove a page at runtime.
survey.removePage(page);
// Move focus to a named question.
survey.focusQuestion("email");
// Reset answers and return to the first page.
survey.clear(true, true);
// Release event handlers and renderer resources.
survey.dispose();
Events
SurveyJS uses model events. A jQuery page can use normal delegated DOM events around the rendered container, but the official form lifecycle lives on the model. The list below groups the events most developers need first. The full API adds specialized events for matrix rows, dynamic panels, file operations, choice APIs, render hooks, focus, quiz checks, and UI state.
// Read final data after the respondent completes the form.
survey.onComplete.add(function(sender) {
console.log(sender.data);
});
// Run code before final completion.
survey.onCompleting.add(function(sender, options) {
console.log("Completing", sender.data);
});
// Save partial results after page navigation.
survey.onPartialSend.add(function(sender) {
console.log(sender.data);
});
// React before the current page changes.
survey.onCurrentPageChanging.add(function(sender, options) {
console.log(options.oldCurrentPage, options.newCurrentPage);
});
// React after the current page changes.
survey.onCurrentPageChanged.add(function(sender) {
localStorage.setItem("draftSurvey", JSON.stringify(sender.data));
});
// React to one changed question value.
survey.onValueChanged.add(function(sender, options) {
console.log(options.name, options.value);
});
// React before a value changes.
survey.onValueChanging.add(function(sender, options) {
console.log(options.name, options.value);
});
// Add custom validation for one question.
survey.onValidateQuestion.add(function(sender, options) {
if (options.name === "workEmail" && !String(options.value || "").includes("@")) {
options.error = "Enter a valid work email.";
}
});
// Validate data on a server before completion.
survey.onServerValidateQuestions.add(function(sender, options) {
fetch("/api/validate-survey", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(options.data)
})
.then(function(response) { return response.json(); })
.then(function(result) {
options.errors = result.errors || {};
options.complete();
});
});
// Add validation for a page.
survey.onValidatePage.add(function(sender, options) {
console.log(options.page);
});
// Add validation for a panel.
survey.onValidatePanel.add(function(sender, options) {
console.log(options.panel);
});
// React when the preview screen opens.
survey.onShowingPreview.add(function(sender, options) {
console.log(options);
});
// Handle custom completion navigation.
survey.onNavigateToUrl.add(function(sender, options) {
console.log(options.url);
});
// Process uploaded files from a file question.
survey.onUploadFiles.add(function(sender, options) {
console.log(options.files);
});
// Process downloaded file values.
survey.onDownloadFile.add(function(sender, options) {
console.log(options.fileValue);
});
// Process cleared file values.
survey.onClearFiles.add(function(sender, options) {
console.log(options.fileName);
});
// Read a loaded choice list.
survey.onChoicesLoaded.add(function(sender, options) {
console.log(options.question.name);
});
// Load choices in pages for large lists.
survey.onChoicesLazyLoad.add(function(sender, options) {
console.log(options);
});
// Resolve display text for a selected remote choice.
survey.onGetChoiceDisplayValue.add(function(sender, options) {
console.log(options);
});
// Filter search results in a choice list.
survey.onChoicesSearch.add(function(sender, options) {
console.log(options);
});
// React when a dropdown opens.
survey.onOpenDropdownMenu.add(function(sender, options) {
console.log(options.question.name);
});
// Handle a user-created choice item.
survey.onCreateCustomChoiceItem.add(function(sender, options) {
console.log(options);
});
// Replace dynamic text values before SurveyJS displays them.
survey.onProcessDynamicText.add(function(sender, options) {
console.log(options.name, options.value);
});
// Adjust rendered question markup after SurveyJS creates it.
survey.onAfterRenderQuestion.add(function(sender, options) {
options.htmlElement.classList.add("survey-question-ready");
});
// Adjust rendered survey markup after SurveyJS creates it.
survey.onAfterRenderSurvey.add(function(sender, options) {
options.htmlElement.classList.add("survey-ready");
});
// Override generated CSS classes for a question.
survey.onUpdateQuestionCssClasses.add(function(sender, options) {
options.cssClasses.root += " custom-question";
});
// Convert Markdown-style text before display.
survey.onTextMarkdown.add(function(sender, options) {
options.html = options.text.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>");
});
// Change a question title before display.
survey.onGetQuestionTitle.add(function(sender, options) {
console.log(options.question.name, options.title);
});
// Change progress text.
survey.onGetProgressText.add(function(sender, options) {
options.text = options.answeredQuestionCount + " answered";
});
// React to a matrix row addition.
survey.onMatrixRowAdded.add(function(sender, options) {
console.log(options.question.name);
});
// React to a matrix cell value change.
survey.onMatrixCellValueChanged.add(function(sender, options) {
console.log(options.columnName, options.value);
});
// React to a dynamic panel item addition.
survey.onDynamicPanelAdded.add(function(sender, options) {
console.log(options.panelIndex);
});
// React to a dynamic panel value change.
survey.onDynamicPanelValueChanged.add(function(sender, options) {
console.log(options.name, options.value);
});
// Check quiz answer correctness from code.
survey.onCheckAnswerCorrect.add(function(sender, options) {
console.log(options.question.name);
});
// Store UI state such as collapsed panels.
survey.onUIStateChanged.add(function(sender, options) {
localStorage.setItem("surveyUiState", JSON.stringify(sender.uiState));
});
// jQuery DOM hook for code that sits around the rendered form.
$("#customerSurvey").on("click", ".sd-btn", function() {
console.log("Survey button clicked");
});
Advanced Examples
Conditional Lead Qualification Form
Use this pattern for marketing forms that ask different follow-up questions after the visitor selects a budget or timeline.
const leadSurveyJson = {
title: "Project Request",
showQuestionNumbers: "off",
elements: [
{
type: "dropdown",
name: "budget",
title: "Estimated project budget",
isRequired: true,
choices: ["Under $2,000", "$2,000 to $10,000", "Over $10,000"]
},
{
type: "comment",
name: "enterpriseNeeds",
title: "List the systems this project must connect to.",
visibleIf: "{budget} = 'Over $10,000'",
isRequired: true
},
{
type: "radiogroup",
name: "timeline",
title: "Preferred start date",
choices: ["This week", "This month", "Next quarter"]
}
]
};
const leadSurvey = new Survey.Model(leadSurveyJson);
leadSurvey.render(document.getElementById("leadForm"));
Scored Quiz Example
Use a quiz schema when the form must calculate a score and show custom completion text.
const quizJson = {
title: "JavaScript Form Quiz",
showQuestionNumbers: "off",
elements: [
{
type: "radiogroup",
name: "q1",
title: "Which format defines a SurveyJS form?",
choices: ["JSON", "PNG", "SQL"],
correctAnswer: "JSON",
isRequired: true
},
{
type: "radiogroup",
name: "q2",
title: "Which event reads submitted answers?",
choices: ["onComplete", "onResize", "onScroll"],
correctAnswer: "onComplete",
isRequired: true
},
{
type: "expression",
name: "score",
title: "Score",
expression: "iif({q1} = 'JSON', 1, 0) + iif({q2} = 'onComplete', 1, 0)"
}
],
completedHtml: "<h3>Score: {score} of 2</h3>"
};
const quizSurvey = new Survey.Model(quizJson);
quizSurvey.render(document.getElementById("quizForm"));
Remote Choice List Example
Use choicesByUrl when a select field must read options from an API endpoint.
const planSurveyJson = {
title: "Plan Selector",
showQuestionNumbers: "off",
elements: [
{
type: "dropdown",
name: "planId",
title: "Choose a plan",
choicesByUrl: {
url: "/api/billing-plans",
valueName: "id",
titleName: "name"
},
isRequired: true
},
{
type: "comment",
name: "requirements",
title: "Add implementation notes."
}
]
};
const planSurvey = new Survey.Model(planSurveyJson);
planSurvey.onComplete.add(function(sender) {
fetch("/api/survey-results", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(sender.data)
});
});
planSurvey.render(document.getElementById("planSurvey"));
Dynamic Service Quote Form
Use a dynamic panel when the user must add repeatable rows, such as services, team members, products, or tasks.
const quoteJson = {
title: "Service Quote",
showQuestionNumbers: "off",
elements: [
{
type: "paneldynamic",
name: "services",
title: "Services",
panelCount: 1,
minPanelCount: 1,
maxPanelCount: 5,
panelAddText: "Add service",
templateElements: [
{
type: "dropdown",
name: "serviceType",
title: "Service type",
choices: ["Audit", "Setup", "Training", "Support"],
isRequired: true
},
{
type: "text",
name: "hours",
title: "Estimated hours",
inputType: "number",
min: 1,
max: 80,
isRequired: true
}
]
}
]
};
const quoteSurvey = new Survey.Model(quoteJson);
quoteSurvey.render(document.getElementById("quoteSurvey"));
Implementation Tips
- Load
survey-corebeforesurvey-js-ui. - Load the SurveyJS stylesheet before rendering the form.
- Use unique question names. Duplicate names overwrite submitted values.
- Pin beta package versions during 3.0 migration checks.
- Call
render()after the target container exists in the DOM. - Check API response shape before using
choicesByUrl. - Use model events for business logic. Use jQuery events for nearby DOM behavior.
- Use
applyTheme()for modern themes. OldStylesManagersnippets belong to legacy examples.
Advanced Extension Points
SurveyJS exposes several extension points for projects that need more than a static questionnaire. The model can run custom expression functions, accept custom JSON properties, localize UI strings, process Markdown-style text, and connect file or choice questions to application services.
// Register a custom expression function.
Survey.FunctionFactory.Instance.register("isEnterpriseEmail", function(params) {
const email = String(params[0] || "");
return email.endsWith("@company.com");
});
// Use the custom function in survey JSON.
const surveyJson = {
elements: [
{
type: "text",
name: "workEmail",
title: "Work email",
validators: [
{
type: "expression",
expression: "isEnterpriseEmail({workEmail})",
text: "Enter a company email address."
}
]
}
]
};
// Add a custom serializable property to questions.
Survey.Serializer.addProperty("question", {
name: "analyticsKey:string",
category: "data"
});
Localization uses the locale property after language files load. File questions use onUploadFiles, onDownloadFile, and onClearFiles. Remote choice lists use choicesByUrl, onChoicesLoaded, onChoicesLazyLoad, and onGetChoiceDisplayValue.
Changelog:
v2.5.33 (2026-07-11)
- Bugfixes and improvements.
v2.5.6 (2026-01-16)
- Separate languages functionality doesn't work correctly for matrix rubric
- Separate languages functionality doesn't work correctly for composite & specified question types
- survey.toJSON with validatePropertyValues options and property with choices where values are objects
- select questions visibleChoices properties are not re-calculated if choice visibleIf depends on an object property
- Question numbering may have incorrect style on showing/hiding/showing the question
- added functionName() method for function expression operand
- Add survey onCreateValidatorRegExp event
- Expressions - The contains operator is always case-sensitive
- Fix ProcessValue hasValue function for design mode
- updated survey-library translation
- SSR and Shadow-Dom compatibility
- Bug/fix element focus for creator logic tab
v2.5.5 (2026-01-08)
- Bugfix & Update
v2.5.4 (2025-12-31)
- Bugfix & Update
v2.5.3 (2025-12-26)
- Bugfix
v2.5.1 (2025-12-17)
- Remove unused disableDesignActions property from the SurveyElement class
- A choice's display text is not rendered in a dynamic panel's tab title when a value name uses uppercase letters
- Fix alternative name for templateQuestionTitleLocation prop (Andrew Telnov)
- A Text input field with "maskType": "numeric" allows entering values regardless readOnly: true
v2.5.0 (2025-12-11)
- Add support for promises in custom functions.
- Allow to access survey elements properties in text processing and expressions.
- Custom numbering in composite question.
- Fix: Survey.focusQuestion doesn't focus the first row of a Multi-Select Matrix.
- Fix: Action dropdown button doesn't support markdown.
- Fix: Matrix cell values persist when columns are hidden.
- Fix: Expand/Collapse button doesn't work for Single-Select Matrix and Multiple Textbox items in inputPerPage mode.
This awesome jQuery plugin is developed by surveyjs. For more Advanced Usages, please check the demo page or visit the official website.











