DEV Community

Cover image for Add Survey Creator / Form Builder to Your React Application
SurveyJS for _SurveyJS

Posted on • Edited on • Originally published at Medium

6 1

Add Survey Creator / Form Builder to Your React Application

The SurveyJS Team has great news for React users! We introduce a major update of our Survey Creator component. It received a new UI, but most importantly, the new Survey Creator for React is a composition of true React components. It no longer depends on non-React JavaScript widgets. This article shows how to integrate the new Survey Creator into your React app.

New Survey Creator for React

We will implement a simple application that displays a list of surveys stored in a database. Users can create new surveys and edit/delete existing surveys.

Task 1: Add Survey Creator to your React app

Step 1: Install Survey Creator. Open CodeSandbox and create a new React application. After that, go to the package.json file and add the survey-creator-react package to dependencies.

Step 2: Create a custom component that renders the Survey Creator. We can name it SurveyCreatorWidget and the file SurveyCreatorWidget.jsx. Import the SurveyCreatorComponent and SurveyCreator model from the survey-creator-react package and import style sheets for the SurveyJS Library and Creator. Instantiate the SurveyCreator model, store the instance in the component state to prevent unnecessary re-renderings, and assign the instance to the SurveyCreatorComponent.

Step 3: Render the custom component. Import SurveyCreatorWidget into the App.js file and render it in the default App() function.

import "./styles.css";
import { SurveyCreatorWidget } from "./SurveyCreatorWidget";
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<SurveyCreatorWidget></SurveyCreatorWidget>
</div>
);
}
view raw App.js hosted with ❤ by GitHub
"dependencies": {
"react": "17.0.2",
"react-dom": "17.0.2",
"survey-creator-core": "latest",
"survey-creator-react": "latest"
},
view raw package.json hosted with ❤ by GitHub
import { useState } from "react";
import { SurveyCreatorComponent, SurveyCreator } from "survey-creator-react";
// Import stylesheets for SurveyJS Library and Survey Creator
import "survey-core/defaultV2.min.css";
import "survey-creator-core/survey-creator-core.min.css";
export function SurveyCreatorWidget(props) {
const [creator, setCreator] = useState(null);
if (!creator) {
// Create a Survey Creator instance
const creator = new SurveyCreator({ showLogicTab: true });
setCreator(creator);
}
const style = { height: "100vh" };
return (
<div style={style}>
<SurveyCreatorComponent creator={creator}></SurveyCreatorComponent>
</div>
);
}

If you do everything right, the Survey Creator will be shown in the CodeSandbox preview:

Empty Survey Creator

Task 2: Show a survey list that supports CRUD operations and set up React routing

This task bears no relation to SurveyJS functionality. We need to get the list of surveys from a database, allow users to create a new survey, and change the name and JSON definition of an existing survey. Unfortunately, in real-world apps, you have to repeat these steps for every application. If you are familiar with them, you can skip this section and go directly to Task 3.

Step 1: Implement asynchronous functions that work with the database. To simplify the code and let you modify data locally in your browser, we will use the browser's local storage and emulate asynchronous calls using the setTimeout function. We will put all our data related functions into the WebDataService.js file. In the code below, this file contains only function signatures for brevity. Refer to the resulting Sandbox for a full code listing.

Step 2: Render the survey list. Create the SurveyListWidget component that will render the list of surveys, allow users to add a new survey and edit/delete existing surveys. It will navigate to the following path for editing surveys: /editsurvey/:id.

Survey list

Step 3: Set up React routing. We use the react-router-dom package for this task. Add it to package.json and configure routing in the App.js file.

import * as React from "react";
import "./styles.css";
import { BrowserRouter, Switch, Route, Link } from "react-router-dom";
import { SurveyListWidget } from "./SurveyListWidget";
import { SurveyCreatorWidget } from "./SurveyCreatorWidget";
export default function App() {
return (
<BrowserRouter>
<div className="App">
<div className="App__header">
<Link to="/">
<h1>SurveyJS service React example</h1>
</Link>
</div>
<Switch>
<Route exact path="/" component={SurveyListWidget} />
<Route
exact
path="/editsurvey/:id"
render={(props) => (
<SurveyCreatorWidget id={props.match.params.id} />
)}
/>
</Switch>
</div>
</BrowserRouter>
);
}
view raw App.js hosted with ❤ by GitHub
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.0",
"survey-creator-core": "latest",
"survey-creator-react": "latest"
},
view raw package.json hosted with ❤ by GitHub
import { useEffect, useState } from "react";
import { getSurveyItems, createSurvey, deleteSurvey } from "./WebDataService";
import { Link } from "react-router-dom";
export function SurveyListWidget(props) {
const [items, setItems] = useState([]);
useEffect(() => {
updateSurveyList();
});
const updateSurveyList = () => {
getSurveyItems((currentItems) => {
setItems(currentItems);
});
};
const addNewSurvey = () => {
createSurvey((newItem) => {
props.history.push("/editsurvey/" + newItem.id.toString());
});
};
const removeSurvey = (id) => {
deleteSurvey(id, (currentItems) => {
setItems([...currentItems]);
});
};
let list = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
list.push(
<li key={item.id}>
<Link to={`/editsurvey/${item.id}`}>{item.name}</Link>
<Link className="edit-link" to={`/editsurvey/${item.id}`}>
<button type="button" className="btn btn--primary">
Edit
</button>
</Link>
<button
type="button"
className="btn btn--danger"
onClick={() => {
removeSurvey(item.id);
}}
>
Remove
</button>
</li>
);
}
if (list.length === 0) {
list.push(<li key={-1}>Your survey list is empty</li>);
}
return (
<div className="survey-list">
<ul>{list}</ul>
<button className="add-btn" type="button" onClick={addNewSurvey}>
Add New Survey
</button>
</div>
);
}
// Get the survey list. Each object contains the following properties: `id`, `name`, and `json`
export function getSurveyItems(onCallback);
// Create a new survey and return it
export function createSurvey(onCallback);
// Delete a survey by `id` and return the updated survey list
export function deleteSurvey(id, onCallback);
// Get a survey JSON definition by survey `id`
export function getSurveyJSON(id, onCallback);
// Get a survey name by survey `id`
export function getSurveyName(id, onCallback);
// Set a survey JSON definition by survey `id`
export function saveSurveyJSON(id, json, onCallback);
// Set a survey name by survey `id`
export function saveSurveyName(id, name, onCallback);

Task 3: Load and save survey JSON definitions

We pass survey ID into the SurveyCreatorWidget as props. All we need to do is call the getSurveyJSON and saveSurveyJSON functions from the WebDataService.js file.

Step 1: Load a survey JSON definition from the database. Since it is an asynchronous operation, we should use the Effect hook to prevent the component from re-rendering when the Survey Creator model changes.

Step 2: Save a survey JSON definition to the database. Survey Creator has an isAutoSave property. If you enable it, the saveSurveyFunc callback is called on every change. The callback has two parameters: a numeric saveNo and a callback function. saveNo is an incremental number. Since web services are asynchronous by their nature, older changes may come after more recent changes. It means that if you saved the change #152 on the server, you can simply ignore changes #151 and below. After getting a confirmation from the server, you can use the callback parameter and call it as callback(saveNo, true) in case of a success or callback(saveNo, false) in case server could not save the data for some reason. In both cases, Survey Creator will show notifications.

import { useEffect, useCallback, useState } from "react";
import { SurveyCreatorComponent, SurveyCreator } from "survey-creator-react";
import { Serializer } from "survey-core";
import { getSurveyJSON, saveSurveyJSON } from "./WebDataService";
// Import stylesheets for SurveyJS Library and Survey Creator
import "survey-core/defaultV2.css";
import "survey-creator-core/survey-creator-core.min.css";
export function SurveyCreatorWidget(props) {
const [creator, setCreator] = useState(null);
if (!creator) {
// Create a Survey Creator instance
const creator = new SurveyCreator({ showLogicTab: true });
// Enable auto save
creator.isAutoSave = true;
// A function executed to save the survey definition
creator.saveSurveyFunc = (saveNo, callback) => {
// You can use `this.creator.text` as an alternative to `this.creator.JSON`
saveSurveyJSON(id(), creator.JSON, () => {
callback(saveNo, true);
});
};
setCreator(creator);
}
const id = useCallback(() => {
let res = props.id;
if (!res) return -1;
return Number(res);
}, [props.id]);
useEffect(() => {
// Load survey definition
getSurveyJSON(id(), (json) => {
// You can use `this.creator.text` as an alternative to `this.creator.JSON`
creator.JSON = json;
});
});
const style = { height: "100vh" };
return (
<div style={style}>
<SurveyCreatorComponent creator={creator}></SurveyCreatorComponent>
</div>
);
}

Task 4: Change the survey name

You can implement different UIs to allow users to change the survey name. For example, users can edit the name in a survey list. The second option is to display a text input for editing the survey name below the Survey Creator. Another solution is to add a survey property that users can modify in the Survey Creator's Property Grid (see the example.

In this article, we take the survey name from the survey title and save a record about it in the database. The record has three fields: id, name, and json.

Survey title

Step 1: Set the survey title. You can do it in the code as follows: creator.survey.title = "yourValue";. Do it in the Effect hook, as we did when we loaded survey JSON definition in task 3.

Step 2: Update the survey name in the database. Set the name field in the database record when the survey title property is changed. You can use the creator.onModified event for this purpose.

Step 3: Make the survey title property required. You should prevent end users from emptying the survey title because the survey name in the database cannot be empty. There are several ways of doing it, but the easiest one is to find the needed property and set its isRequired attribute to true.

import { useEffect, useCallback } from "react";
import { Link } from "react-router-dom";
import { SurveyCreatorComponent, SurveyCreator } from "survey-creator-react";
import { Serializer } from "survey-core";
import {
getSurveyJSON,
saveSurveyJSON,
getSurveyName,
saveSurveyName
} from "./WebDataService";
// Import stylesheets for SurveyJS Library and Survey Creator
import "survey-core/defaultV2.css";
import "survey-creator-core/survey-creator-core.min.css";
// Make survey title required. End users cannot make it empty in designer or property grid
Serializer.findProperty("survey", "title").isRequired = true;
export function SurveyCreatorWidget(props) {
// Create a Survey Creator instance
const creator = new SurveyCreator({ showLogicTab: true });
// Enable auto save
creator.isAutoSave = true;
// A function executed to save the survey definition
creator.saveSurveyFunc = (saveNo, callback) => {
// You can use `this.creator.text` as an alternative to `this.creator.JSON`
saveSurveyJSON(id(), creator.JSON, () => {
callback(saveNo, true);
});
};
creator.onModified.add((_, options) => {
// We are interested in property changes only
if (options.type === "PROPERTY_CHANGED") {
// Update the survey name in the database when the survey title is changed
if (
options.name === "title" &&
!!options.target &&
options.target.getType() === "survey"
) {
saveSurveyName(id(), options.newValue);
}
}
});
const id = useCallback(() => {
let res = props.id;
if (!res) return -1;
return Number(res);
}, [props.id]);
useEffect(() => {
// Load survey definition
getSurveyJSON(id(), (json) => {
// Save the survey title to prevent it from being overwritten
const prevTitle = creator.survey.title;
// You can use `this.creator.text` as an alternative to `this.creator.JSON`
creator.JSON = json;
if (!!prevTitle) {
creator.survey.title = prevTitle;
}
});
// Assign the survey name from the database to the survey title
getSurveyName(id(), (name) => {
creator.survey.title = name;
});
});
const style = { height: "100vh" };
return (
<div style={style}>
<Link to="/">
<button type="button" className="btn btn--primary back-btn">
Back To List
</button>
</Link>
<SurveyCreatorComponent creator={creator}></SurveyCreatorComponent>
</div>
);
}

Conclusion

You have learnt how to add our Survey Creator component into your React application and save survey JSON definitions in a database. It is not a complete survey service. Missing capabilities include showing surveys to end users, gathering survey responses, and presenting them in a table or dashboard. Leave a comment below if you want us to continue implementing the service in our future articles.

About SurveyJS Project

SurveyJS Project includes four open-source JavaScript Libraries:

  • SurveyJS Library - embeds and runs surveys on your websites. (Available for free under the MIT license).
  • SurveyJS Creator - a survey / form builder component that you can embed into your websites. (Requires a commercial developer license).
  • SurveyJS Analytics Pack - displays survey responses in a dashboard. (Requires a commercial developer license).
  • SurveyJS Export to PDF - exports survey responses as PDF files. (Requires a commercial developer license).

To learn more about SurveyJS Project, visit our website: surveyjs.io.

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

Neon image

Next.js applications: Set up a Neon project in seconds

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Get started →

👋 Kindness is contagious

DEV works best when you're signed in—unlocking a more customized experience with features like dark mode and personalized reading settings!

Okay