Save additional JavaScript data
Use a named Flow result when a value should appear in conditions, question text, or result columns. Use globals or questionState for extra JSON data such as a list of trial results.
Despite its name, globals belongs to one participant response, not all participants. questionState belongs to one question within that response. Both survive a reload after a successful save. Ordinary JavaScript variables do not.
In Preview, these stores are in memory. Reset test answers or reloading Preview clears them; they do not become participant responses.
Available calls
| Call | Purpose |
|---|---|
globals.get("key") |
Reads saved data, or undefined if the key does not exist. |
globals.init({key: value}) |
Sets defaults for missing keys, preserving existing values. |
globals.set("key", value) |
Saves or replaces a value. |
Question scripts also have questionState.get, questionState.init, and questionState.set with the same behavior, limited to that question. Question state is shared across visits to the same question occurrence. Each Loop occurrence has its own saved question data.
Values can be JSON objects, arrays, strings, finite numbers, booleans, or null. Functions, undefined, dates, DOM elements, circular objects, and non-finite numbers cannot be saved. Reads return copies: changing a returned object does not save it.
Save data in a Flow calculation
Configure a Number result named trial_count:
const previous = globals.get("trials") ?? [];
const updated = [...previous, {score: variables.get("score")}];
globals.set("trials", updated);
return updated.length;
Declare the input score earlier in Flow. This saves the trial list and returns its new length. In Flow calculations, writes are staged: do not use a later globals.get("trials") in the same node to read the updated list. Keep updated in a local variable instead.
On the first successful run, trial_count is 1. Display it on a later page with Insert placeholder → trial_count to check the calculation in Preview. Going Back to before this step and crossing it again can append another record; this example counts executions, not unique trials. Failed attempts discard staged writes, and a transport retry of an already accepted save does not append another record.
All results and global writes from a Flow node save together after every calculation succeeds. If one calculation fails, none of those changes are saved.
Save data in a question script
Open a question’s Custom JavaScript, enable it, and enter:
async function onLoad(question, variables) {
await questionState.init({visits: 0});
const visits = questionState.get("visits");
await questionState.set("visits", visits + 1);
}
The first visit saves 1; returning to the question saves 2. Unlike Flow staging, an awaited question-data write updates subsequent reads. Await every write so your code notices a rejected save. A later script failure does not undo earlier acknowledged question-data writes.
To check the saved object, submit a published pilot response and inspect JavaScript · Questions in Results → Export preview. Test responses in Preview do not appear there.
Export and privacy
Completed-response exports include JSON columns JavaScript · Globals and JavaScript · Questions. These stores are not automatically available as Flow conditions or text placeholders. Save a typed Flow result as well when you need those uses.
Store only data needed for the study. JavaScript data follows response access and personal-data deletion rules. Avoid saving a complete browser fingerprint or sending participant answers to an external service unless the study requires it and your participant information covers that collection.
See storage limits and question scripts.