blog

Build Offline Web Forms with SurveyJS (No Internet Required)

We frequently get asked: 💡 "Can SurveyJS work without an internet connection?" The answer is absolutely yes, and it's more important than you might think.

In general, we see two main groups asking this:

  • Field research and data collection companies whose clients operate in remote areas with no internet infrastructure—places where connectivity is limited or completely unavailable.
  • Security-conscious enterprises where internet access is restricted or completely prohibited due to compliance, security policies, or air-gapped network environments.

For these scenarios, SurveyJS is a natural fit. SurveyJS form builder can work entirely offline and sync to the server only when needed while retaining full functionality.

In this post, we'll explore how you can integrate SurveyJS form builder into your application running on a client's machine with no internet connection. We'll walk through a live demo built with Preact and demonstrate how easily you can store a survey and its theme locally, no backend required, and sync data when you're ready.

Real-World Use Cases for Offline Form and Survey Builder

Industry Scenario
Healthcare Staff create or modify patient intake and diagnostic forms in clinics without internet access.
Education Teachers prepare quizzes, feedback forms, or lesson plans in offline environments.
Events Organizers adjust registration or feedback forms on-site before or during the event.
Legal / Compliance Auditors work with sensitive internal forms in air-gapped environments to meet strict data policies.
Government Officials design public service or census forms on secure offline systems.

Why SurveyJS Is a Perfect Fit for Air-Gapped Networks

SurveyJS Creator is built to help you design, deploy, and run surveys entirely on your device without a constant internet connection. It offers a smooth, local-first experience that lets you:

  • Build and edit surveys and forms directly in your browser, no server required.
  • Store your surveys and collected data securely using the browser storage options.
  • Operate fully offline, so you're never blocked by connectivity issues.
  • Sync your survey data and updates back to your server whenever you're ready.
  • Integrate SurveyJS easily with any web application framework you use.

This means you get a reliable, flexible offline form and survey builder that adapts to your workflow, wherever you work.

Try the Demo: Offline SurveyJS Survey Creator

View on GitHub

We've built an example which demonstrates SurveyJS Creator working entirely offline. The demo includes:

  • Full form builder interface powered by SurveyJS Creator (including the Localization and Theme editors)
  • Automatic local storage of the survey and theme JSON schemas
  • Complete offline functionality (no internet connection required)
  • Optional sync capability: uploads to a server when the internet is available

SurveyJS libraries are loaded from local files, so everything runs completely offline. No CDNs. No server requests. Just open the HTML file in your browser and start building.

The application built with Preact for minimal overhead, but the same approach works in React, Vue, Angular, or plain JavaScript.

Complete Offline Functionality

The demo works with zero internet connection. To load SurveyJS resources, download them from npm:

Save them locally to the application folder. Load SurveyJS resources, including localization and themes, from the local app folder as follows:

<script src="./survey.core.js"></script>
<script src="./survey.i18n.js"></script>
<script src="./survey-js-ui.js"></script>
<script src="./themes/index.js"></script>
<script src="./creator-themes/index.js"></script>
<script src="./survey-creator-core.js"></script>
<script src="./survey-creator-core.i18n.js"></script>
<script src="./survey-creator-js.js"></script>

You can now create and design surveys, localize form content, and build custom survey themes—all with your changes automatically saved. Simply close and reopen your browser to pick up right where you left off.

Save Changes to Local Storage

Implement the saveSurveyFunc function to store the edited survey in localStorage. When initializing Survey Creator, check whether localStorage already contains a survey. If it does, restore the survey's JSON schema and assign it to the JSON property.

const LOCAL_STORAGE_KEY = "localSurveyJSON";

// Restore a saved survey schema
const savedJSON = localStorage.getItem(LOCAL_STORAGE_KEY);
if (savedJSON) {
   try {
    creator.JSON = JSON.parse(savedJSON);
   } catch (e) {
      console.warn("Failed to parse saved JSON:", e);
   }
}
// Save the survey schema
creator.saveSurveyFunc = (saveNo, callback) => {
    try {
      const currentJSON = creator.JSON;
      localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(currentJSON));
      isSurveySaved = true;
      console.log("Survey saved.");
    } catch (e) {
      console.error("Failed to save survey JSON:", e);
      isSurveySaved = false;
    }
    markSyncedIfBothSaved();
    callback(saveNo, true);
};

Similarly, users can edit a survey theme in the Theme Editor. To persist the theme, implement the saveThemeFunc function and store the edited theme in localStorage. On initialization, check if a saved theme exists. If so, restore its JSON schema and assign it to the theme property.

const THEME_STORAGE_KEY = "localSurveyTheme";

// Restore a saved theme
const savedTheme = localStorage.getItem(THEME_STORAGE_KEY);
  if (savedTheme) {
    try {
      creator.theme = JSON.parse(savedTheme);
    } catch (e) {
      console.warn("Failed to parse saved theme:", e);
    }
}
// Save the theme 
creator.saveThemeFunc = (saveNo, callback) => {
    try {
      const currentTheme = creator.theme;
      localStorage.setItem(THEME_STORAGE_KEY, JSON.stringify(currentTheme));
      isThemeSaved = true;
      console.log("Theme saved.");
    } catch (e) {
      console.error("Failed to save theme:", e);
      isThemeSaved = false;
    }
    markSyncedIfBothSaved();
    callback(saveNo, true);
  };

By default, the Form Builder displays a Save button that allows users to save edits manually. For convenience, you can enable auto-save by setting the autoSaveEnabled property to true. With this setting, the Form Builder automatically calls saveSurveyFunc or saveThemeFunc whenever survey or theme setting change, with a default delay of 500 ms. You can adjust this interval using the autoSaveDelay property.

Storage Synchronization

When internet connectivity is restored, the app can automatically sync local data:

window.addEventListener("online", () => {
  const isSynced = localStorage.getItem(SYNCED_FLAG_KEY);
  const json = localStorage.getItem(LOCAL_STORAGE_KEY);
  const theme = localStorage.getItem(THEME_STORAGE_KEY);

  if (isSynced !== "true" && json && theme) {
    console.log("Syncing with server...");
    sendToServer(JSON.parse(json), theme);
  }
});

While our demo uses localStorage for simplicity, production applications can explore more robust options such as IndexedDB or integration with backend or cloud APIs.

View Full Code
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>SurveyJS Form Builder (pReact version)</title>

  <script src="./survey.core.js"></script>
  <script src="./survey.i18n.js"></script>
  <script src="./survey-js-ui.js"></script>
  <script src="./themes/index.js"></script>
  <script src="./creator-themes/index.js"></script>
  <script src="./survey-creator-core.js"></script>
  <script src="./survey-creator-core.i18n.js"></script>
  <script src="./survey-creator-js.js"></script>

  <link rel="stylesheet" href="./survey-core.css" />
  <link rel="stylesheet" href="./survey-creator-core.css" />
</head>

<body>
  <div id="surveyCreatorContainer" style="position: fixed; top: 0; left: 0; right: 0; bottom: 0;"></div>

<script>
  const LOCAL_STORAGE_KEY = "localSurveyJSON";
  const THEME_STORAGE_KEY = "localSurveyTheme";
  const SYNCED_FLAG_KEY = "localSurveySynced";

  let isSurveySaved = false;
  let isThemeSaved = false;

  SurveyCreatorCore.registerCreatorTheme(SurveyCreatorTheme);
  SurveyCreatorCore.registerSurveyTheme(SurveyTheme);

  const creator = new SurveyCreator.SurveyCreator({
    showThemeTab: true,
    showTranslationTab: true
  });

  creator.autoSaveEnabled = true;

  // Restore saved JSON
  const savedJSON = localStorage.getItem(LOCAL_STORAGE_KEY);
  if (savedJSON) {
    try {
      creator.JSON = JSON.parse(savedJSON);
    } catch (e) {
      console.warn("Failed to parse saved JSON:", e);
    }
  }

  // Restore theme
  const savedTheme = localStorage.getItem(THEME_STORAGE_KEY);
  if (savedTheme) {
    try {
      creator.theme = JSON.parse(savedTheme);
    } catch (e) {
      console.warn("Failed to parse saved theme:", e);
    }
  }


  function markSyncedIfBothSaved() {
    if (isSurveySaved && isThemeSaved) {
      localStorage.setItem(SYNCED_FLAG_KEY, "true");
      console.log("Both survey and theme saved. Synced flag set.");
    } else {
      localStorage.setItem(SYNCED_FLAG_KEY, "false");
    }
  }

  // Persist a survey JSON
  creator.saveSurveyFunc = (saveNo, callback) => {
    try {
      const currentJSON = creator.JSON;
      localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(currentJSON));
      isSurveySaved = true;
      console.log("Survey saved.");
    } catch (e) {
      console.error("Failed to save survey JSON:", e);
      isSurveySaved = false;
    }
    markSyncedIfBothSaved();
    callback(saveNo, true);
  };
  
  // Persist a survey theme
  creator.saveThemeFunc = (saveNo, callback) => {
    try {
      const currentTheme = creator.theme;
      localStorage.setItem(THEME_STORAGE_KEY, JSON.stringify(currentTheme));
      isThemeSaved = true;
      console.log("Theme saved.");
    } catch (e) {
      console.error("Failed to save theme:", e);
      isThemeSaved = false;
    }
    markSyncedIfBothSaved();
    callback(saveNo, true);
  };

  window.addEventListener("online", () => {
    const isSynced = localStorage.getItem(SYNCED_FLAG_KEY);
    const json = localStorage.getItem(LOCAL_STORAGE_KEY);
    const theme = localStorage.getItem(THEME_STORAGE_KEY);

    if (isSynced !== "true" && json && theme) {
      console.log("Syncing with server...");
      sendToServer(JSON.parse(json), theme);
    }
  });

  function sendToServer(surveyData, theme) {
    // Simulated async sync
    setTimeout(() => {
      console.log("Synced survey:", surveyData);
      console.log("Synced theme:", theme);
      localStorage.setItem(SYNCED_FLAG_KEY, "true");
    }, 1000);
  }

  creator.render("surveyCreatorContainer");
</script>

</body>
</html>

See Also

Refer to the following page for more examples on how to integrate SurveyJS with different backends:

Backend Integration Demos

Your cookie settings

We use cookies to make your browsing experience more convenient and personal. Some cookies are essential, while others help us analyse traffic. Your personal data and cookies may be used for ad personalization. By clicking “Accept All”, you consent to the use of all cookies as described in our Terms of Use and Privacy Statement. You can manage your preferences in “Cookie settings.”

Your renewal subscription expires soon.

Since the license is perpetual, you will still have permanent access to the product versions released within the first 12 month of the original purchase date.

If you wish to continue receiving technical support from our Help Desk specialists and maintain access to the latest product updates, make sure to renew your subscription by clicking the "Renew" button below.

Your renewal subscription has expired.

Since the license is perpetual, you will still have permanent access to the product versions released within the first 12 month of the original purchase date.

If you wish to continue receiving technical support from our Help Desk specialists and maintain access to the latest product updates, make sure to renew your subscription by clicking the "Renew" button below.