Questionnaire Lifecycle
A questionnaire goes through several transformations between the moment a content editor creates it in Strapi and the moment an end user sees it in the browser. Understanding this lifecycle helps you debug issues and know which layer is responsible for what.
The Full Journey
Section titled “The Full Journey”flowchart TD
A["1. Content editor creates<br/>questionnaire in Strapi"] --> B["2. Strapi stores structured<br/>data (metadata, questions,<br/>rules, calculated fields)"]
B --> C["3. Strapi API transforms<br/>data to canonical format"]
C --> D["4. Sync script fetches<br/>via API, wraps strings<br/>in t() for i18n"]
D --> E["5. Generated .ts file<br/>committed to repo"]
E --> F["6. App imports file,<br/>passes TFunction +<br/>interpolation data"]
F --> G["7. qnr-framework renders<br/>questions one at a time"]
G --> H["8. User answers →<br/>rules evaluated →<br/>next question shown"]
Step by Step
Section titled “Step by Step”1. Creating the Questionnaire in Strapi
Section titled “1. Creating the Questionnaire in Strapi”In the Strapi admin, a content editor creates a new Questionnaire entry with four main sections:
| Section | What it contains |
|---|---|
| Metadata | Vertical link, question list (ordering), progress bar config, constants (reusable values), feature flags |
| Questions (dynamic zone) | Each question as a component — with its type, questionId, groupId, props, screen config, and required flag |
| Rules (dynamic zone) | Conditional navigation logic — “if X, go to Y” |
| Calculated fields | Derived values computed from answers (used in rules or display) |
2. How a Question Is Structured
Section titled “2. How a Question Is Structured”Every question in the dynamic zone has these key properties:
| Property | Purpose | Example |
|---|---|---|
| questionId | Unique identifier within the questionnaire | dateOfBirth, coverageStartDate |
| type | The kind of input or screen (30+ types available) | RADIO, DATE, ADDRESS, QUOTE, BLOCKER |
| groupId | Which phase of the flow it belongs to | intro, preQuote, postQuote, signup |
| required | Whether the user must answer to complete the flow | true / false |
| screen | Display configuration: title, subtitle, description | Title: “When would you like coverage to start?” |
| props | Type-specific configuration | Radio options, date constraints, validation rules |
The groupId determines when the question appears relative to the quote page:
flowchart LR
A["<b>intro</b><br/>Welcome screens,<br/>initial context"] --> B["<b>preQuote</b><br/>Questions needed<br/>before pricing"]
B --> C["<b>Quote Page</b><br/>Shows plans,<br/>pricing, addons"]
C --> D["<b>postQuote</b><br/>Additional details<br/>after plan selection"]
D --> E["<b>signup</b><br/>Checkout, payment,<br/>processing, success"]
3. Strapi Transforms the Data
Section titled “3. Strapi Transforms the Data”When the sync script calls the Strapi API, it does not get the raw database format. The questionnaire.js service in Strapi applies several transformations to produce a “canonical” format:
- Constants: The
meta.constantsarray of{key, value}pairs becomes a flat object{key: value} - Progress bar: The array of
{initialQuestionId, title}entries becomes a mapping structure the framework can use - Questions: Dynamic zone components are flattened — the Strapi
__componentfield is replaced with a cleantypefield - Rules: Rule components are simplified —
questionIdbecomesid,outcomebecomesthen
These transforms exist so the CMS can use a rich editing experience (dynamic zones, nested components) while the app receives a clean, flat structure.
4. Sync Script Generates TypeScript
Section titled “4. Sync Script Generates TypeScript”The sync script fetches the transformed data and performs one crucial additional step: it wraps all translatable strings with t() function calls.
For example, a question title like "When were you born?" becomes:
t('dateOfBirth.title', 'When were you born?')This enables the i18n (internationalization) system — the first argument is the translation key, the second is the fallback default. The output is a .ts file in apps/app/src/syncedData/.
5. App Imports and Resolves
Section titled “5. App Imports and Resolves”The generated file exports a function that takes a TFunction (from the i18n library) and optional interpolationData. When the app calls this function, all the t() wrappers resolve to actual translated strings.
6. qnr-framework Renders the Flow
Section titled “6. qnr-framework Renders the Flow”With the resolved questionnaire definition in hand, qnr-framework takes over:
- Find the next question: Looks through the question list for the first unanswered required question
- Render the screen: Shows the question’s screen (title, subtitle, description) along with the appropriate input component
- Wait for the answer: User interacts with the component (selects a radio option, enters a date, etc.)
- Handle the answer: The
onAnswercallback stores the answer in state - Evaluate rules: Checks if any rules for this question should redirect the flow
- Advance: Moves to the next question (either from rules or the default question list order)
7. Rules Control Navigation
Section titled “7. Rules Control Navigation”After each answer, the rules engine checks all rules associated with the current question. If a rule’s condition matches, the flow jumps to a different question instead of following the default order. This is how Birdhouse creates branching flows — for example, showing a blocker screen if the user is under 18, or skipping questions that do not apply to their situation.
See Rule Evaluation for the full details on how rules work.
Common Debugging Scenarios
Section titled “Common Debugging Scenarios”| Symptom | Likely cause | Where to look |
|---|---|---|
| Question not showing | Missing from questionList in metadata, or marked not required with no rule leading to it |
Strapi → Questionnaire → Metadata |
| Question shows at wrong time | Wrong groupId or incorrect position in questionList |
Strapi → Question component |
| Flow skips a question | A rule is redirecting past it | Strapi → Rules, or the generated .ts file |
| Text not appearing or showing a key | Translation key missing or t() wrapper issue |
Generated .ts file, translation files |
| Changes not visible in app | Sync not run, or synced file not deployed | Run sync script, check PR status |
See also
Section titled “See also”- Your First Questionnaire – build a questionnaire from scratch
- Questionnaire Metadata Reference – all fields in the metadata section
- Strapi Transform Pipeline – detailed look at forward and reverse transforms