How to Build Data-Driven Surveys With React + REST API + SurveyJS

Build dynamic forms and surveys in React with the open-source SurveyJS and RESTful services.

ā€¢

image

Surveys are a great way to gather the information you need to better satisfy your user base, and then break that data down into whatever easily trackable indices you need ā€” for patterns that your stakeholders will find useful, or just for internal monitoring over time.

SurveyJS is a free and open-source (FOSS) JavaScript library that lets you do all of that ā€” but goes one step further ā€” using a dynamic, data-driven approach using industry-standard JSON as common ground: you define your surveys as JSON data models (ā€œschemasā€) and build form UI elements from templates that use said data models.

But hereā€™s what will *really *blow your mind.

SurveyJS lets you go all-in on dynamicity, and have any values be populated from a REST API. Your data sources can be defined in the schema itself, via the choicesByUrl property. Say goodbye to code bloat resulting from lines of asynchronous XMLHttpRequests!

The Demo

To demonstrate just how much convenience this brings to *anyone *designing surveys, letā€™s build a food habits survey for a fictitious restaurant that needs to gauge favorite foods and service expectations from a potential customer base.

This example will serve as a great starting point for your own surveys in nutritional counseling, market research for restaurants and fast food joints, or for just observing differences in attitudes when it comes to people dining out.

Getting Started

First off, install the SurveyJS client-side libraries for your framework of choice ā€” React, Angular, Vue.js, Knockout ā€” or follow these instructions here for **jQuery **if you donā€™t use a framework. Weā€™re using React for our code example.

Now, hereā€™s the game plan:

  1. Weā€™ll use SurveyJSā€™ feature of populating choices from a RESTful service,

  2. Utilizing two REST APIs, the RESTCountries API, and TheMealDB API. Both have a free, public tier that doesnā€™t need authentication.

  3. A survey for a restaurant that aims to be a hot, happening, cultural center needs to reflect that same energy. So weā€™ll use extensive customization with CSS to turn our utilitarian-looking survey intoā€¦.this!

image

Before and After.Before and After.

But Firstā€¦Getting Some REST.

Before we start, letā€™s quickly look at how SurveyJS RESTful service works using two test cases ā€” the two APIs weā€™ll be using. One, for geolocation, the popular RESTCountries API, and two, for our main use-case, TheMealDB API.

First, geolocation. The response format for this specific REST API is like this:

[
  {
    "name": {
      "common": "Australia",
      "official": "Commonwealth of Australia",
      "nativeName": {
        "eng": {
          "official": "Commonwealth of Australia",
          "common": "Australia"
        }
      }
    }
  }
]

If we wanted the common name of the country, ā€œAustraliaā€, weā€™d access this property in this JSON object with name.common.

So in our schema, weā€™ll just define our data source in the choicesByUrl property, specifying valueName to access data like so:

{
  "elements": [
    {
      "type": "dropdown",
      "name": "country",
      "title": "Select a country",
      "isRequired": true,
      "choicesByUrl": {
        "url": "https://restcountries.com/v3.1/all?fields=name",
        "valueName": "name.common"
      }
    }
  ]
}

Simple enough, right? But what if our top-level property was an array instead of a standard JSON object? How do we define this in our survey schema? We clearly canā€™t do meals.strArea, as we can see in our second API example here:

{
  "meals": [
    {
      "strArea": "American"
    },
    {
      "strArea": "British"
    },
    {
      "strArea": "Canadian"
    },
    {
      "strArea": "Chinese"
    }
  ]
}

Thatā€™s where the path property comes in!

{
  "elements": [
    {
      "type": "dropdown",
      "name": "question4",
      "title": "What's your favorite cuisine?",
      "isRequired": true,
      "choicesByUrl": {
        "url": "https://www.themealdb.com/api/json/v1/1/list.php?a=list",
        "path": "meals",
        "valueName": "strArea"
      }
    }
  ]
}

Together with valueName, you can access any data within a REST APIā€™s response.

Finally, weā€™re using SurveyJS' ImagePicker field type, which populates a grid of images to choose from ā€” said choices coming from a REST API, of course. In addition to path, and valueName, here are the final properties youā€™ll come across while writing this schema ā€” titleName, and imageLinkName. They work the exact same way in principle; you specify the properties in the response JSON object that includes the title for that image, and the actual image URL, respectively.

{
  "meals": [
    {
      "strMeal": "Krispy Kreme Donut",
      "strMealThumb": "https://www.themealdb.com/images/media/meals/4i5cnx1587672171.jpg",
      "idMeal": "53015"
    }
  ]
}

Our API response format for this has the image title in strMeal and image URL in strMealThumb, soā€¦

{
  "type": "imagepicker",
  "name": "question6",
  "title": "What's a dessert you'd always order? Feel free to skip if you don't usually order these.",
  "choicesByUrl": {
    "url": "https://www.themealdb.com/api/json/v1/1/filter.php?c=Dessert",
    "path": "meals",
    "titleName": "strMeal",
    "imageLinkName": "strMealThumb"
  },
  "showLabel": true
}

ā€¦those are our required values for titleName and imageLinkName, respectively.

On To The Fun Stuff!

Got all of that? Good. Letā€™s get to coding.

1. The Survey Schema

Weā€™ve got 9 questions across multiple pages, some of which populate choices from the REST APIs weā€™ve just talked about. There are some other things going on here that merit a second look:

  • Custom Variables. You can store responses to each question (including Question 9 ā€” which asks respondents to drag-and-drop to arrange facets of service in order of importance) in JSON variables, and use them wherever ā€” and however ā€” in the survey you want. This could be used to determine subsequent question strings (as in Question 4, which uses Question 3ā€™s answer), logic, and yes ā€” even other REST API calls!

  • Conditional Logic. Question 2 (country) is hidden and only pops in if a value in Question 1 (region) has been picked.

  • Dynamic HTML generation on-the-fly post-survey completion, using the top-level completedHtml property. Weā€™re using it here to show chosen answers in a , but you could even use logic to determine HTML conditionally via the completedHtmlOnCondition property ā€” generating markup based on answers!

Whatā€™s more, if you wanted to, you could even use conditional logic to do dynamic redirects to URLs based on certain answers! For example, sending the respondent to your company siteā€™s regional subdomain depending on which region they picked.

ā€œnavigateToUrlOnConditionā€: [

{

ā€œexpressionā€: ā€œ{question1} = ā€˜asiaā€™ā€,

ā€œurlā€: ā€œhttps://yourcompanysite.com/region/asia"

}

]

2. The React App

Nothing special here. We import the schema (in a real-world example, youā€™d retrieve this from a proxy server serving as your backend), then use it to generate a survey using components in SurveyJSā€™ React library.

You have your survey answers in JSON format in the onComplete trigger, and you can use it to do whatever you want post-survey completion. Whether thatā€™s sending HTTP POST requests with it as payload to trigger custom behavior off-site (i.e. a webhook), saving it to a database, or creating visualizations on-the-fly using it; go wild.

Weā€™ve also got some basic XSS protection here via Reactā€™s default data binding (curly braces).

Finally, hereā€™s our CSS:

Go ahead and read up this particular section in SurveyJS documentation to know which CSS classes you can override with your custom CSS.

Hereā€™s how our buttons work, for example:

const surveyCss = {
  actionBar: {
    root: "sd-action-bar custom-actionbar-root",
  },
  navigation: {
    complete: "sd-btn--action sd-navigation__complete-btn custom-btn",
    prev: "sd-navigation__prev-btn custom-btn",
    next: "sd-navigation__next-btn custom-btn",
    start: "sd-navigation__start-btn custom-btn",
    preview: "sd-navigation__preview-btn custom-btn",
  },
};

return (
  <div>
    <Survey model={survey} css={surveyCss} />
  </div>
);
.custom-actionbar-root {
  display: flex;
  align-items: center;
  justify-content: center;
}

.custom-btn {
  background-color: var(--color-btn);
  color: var(--color-cultured);
  border-radius: 30px;
  font-family: var(--font-body-2);
  font-size: 1.5rem;
  font-weight: 100;
  transition: 700ms;
}

.custom-btn:not(:disabled):hover,
.custom-btn:not(:disabled):focus {
  /* glow here */
  box-shadow: var(--box-shadow-blue-glow);
}

image

Cause and Effect.Cause and Effect.

A Dynamic, ā€˜Building Blocksā€™ Approach

And thatā€™s all, folks! Hopefully, now you have a pretty decent idea of how to create awesome-looking, flexible, dynamic surveys without breaking the bank for custom widget libraries, or rolling your own HTML/CSS/JS for bespoke solutions that do not scale, and drain company resources to create, update, and maintain.

So using SurveyJS, weā€™ve made sure that:

  1. You are not tied down to the technology stack between used; JavaScript, Python, etc. with no room for flexibility if technical requirements change. JSON and REST are *universal *data interchange formats/standards ā€” use whatever frontend, server, or database tech stack you want.

  2. You can make quick changes and additions to your surveys regardless of technical expertise, as JSON is well-structured, easy-to-understand, and editing it is just like editing a text file.

  3. You can customize your surveys visually as you see fit now that your design layer is entirely decoupled from the data and logic. Get creative with fully custom styling ā€” whether thatā€™s CSS, SCSS, CSS-in-JS, whichever fits your use case ā€” or use one of the many themes that ship with SurveyJS.

Thatā€™s what makes it so powerful. Itā€™s an elegant, loosely-coupled, building-blocks approach to survey/form creation that brings scalability, and simplifies code maintenance.

Enjoyed this article?

Share it with your network to help others discover it

Continue Learning

Discover more articles on similar topics