System Architecture
Birdhouse is made of two parts:
- A visual interface in Strapi CMS — where non-technical teams (Ops, BizDev, Product, Marketing) assemble reusable building blocks to configure questionnaires, policy detail pages, and vertical metadata
- A technical solution built with code — a sync pipeline that pulls CMS content into the app, the qnr-framework rendering library, and app-level components that bring configurations to life in the browser
Before Birdhouse, all of these building blocks lived entirely in code maintained by Engineering. Adding a question, changing a policy page section, or updating a vertical’s branding required a pull request. Now these are reusable components in Strapi that can be assembled without technical expertise.
Under the hood, the technical side is a pipeline of three systems that each handle a different concern: authoring, building, and rendering. Understanding how they connect helps you reason about where to make changes and what happens when something goes wrong.
The Three Systems
Section titled “The Three Systems”1. Strapi CMS — The Authoring Layer
Section titled “1. Strapi CMS — The Authoring Layer”URL: strapi.product.f14e.com/admin
Strapi is a headless CMS where Product and Ops teams define everything about a questionnaire: its questions, rules, quote pages, pricing, and verticals.
The Strapi instance is heavily customized for Birdhouse:
| Aspect | Details |
|---|---|
| Content types | 28 types including Questionnaire, Vertical, Quote-Page, Quote-Price, Table, Policy-Page, and more |
| Question components | 55+ component types in the questions dynamic zone — from simple inputs (radio, date, text) to complex screens (quote, checkout, processing) |
| Custom plugins | questionid-selector (autocomplete for question IDs), quote-price selectors, preview-button, and others |
| API transforms | Custom service layer (questionnaire.js) that reshapes data before the API returns it |
When you edit a questionnaire in Strapi, you are working with a structured data model — not writing code. The CMS enforces validation, provides a visual editor, and manages draft/published states.
2. Sync Pipeline — The Build Layer
Section titled “2. Sync Pipeline — The Build Layer”The sync pipeline is a set of CLI scripts that bridge the CMS and the React app. They pull data from the Strapi API and generate TypeScript files that the app can import directly.
Key scripts:
| Script | Purpose |
|---|---|
sync:questionnaire |
Fetches a questionnaire from Strapi and generates a .ts file in apps/app/src/syncedData/ |
sync:questionnaire:reverse |
Reads a local .ts file and pushes changes back to Strapi |
sync:vertical |
Syncs vertical definitions, generating TypeScript lookup maps |
The generated files are checked into the repository. This means a questionnaire change goes through the normal code review and deployment process, providing a safety net before changes reach production.
3. qnr-framework — The Rendering Layer
Section titled “3. qnr-framework — The Rendering Layer”@getpopsure/qnr-framework is a React library that takes a questionnaire definition (the generated TypeScript file) and renders it as an interactive, multi-step flow in the user’s browser.
Core concepts:
| Concept | Role |
|---|---|
| Questionnaire | The top-level React component that orchestrates the entire flow |
| QuestionnaireRouter | Manages URL-based navigation between questions |
| Rules engine | Evaluates conditional logic on each answer to determine the next question |
| State manager | Tracks all user answers and determines which questions are complete |
The app extends qnr-framework with custom components specific to Feather’s needs — things like GenericCheckout (Stripe integration), Processing (backend policy creation), and various custom quote page components.
The Three Builders
Section titled “The Three Builders”Birdhouse provides three distinct builders, each with its own set of building blocks in Strapi:
Questionnaire Builder
Section titled “Questionnaire Builder”The most mature builder. It provides building blocks for defining multi-step flows:
| Building block | Examples |
|---|---|
| Question types | Radio, date, text, address, number, currency, and 50+ others |
| Screens | Quote page, checkout, review, blocker, intro, success |
| Rules | Conditional logic that controls navigation between questions |
| Calculated fields | Derived values computed from user answers |
| Validations | Input constraints applied to individual questions |
These building blocks can be combined to create signup, claim, adjustment, and cancellation flows for any vertical.
Policy Details Page Builder
Section titled “Policy Details Page Builder”Configures what users see when managing an existing policy in the app:
| Building block | Examples |
|---|---|
| Sections | Groups of information displayed on the policy page |
| Buttons | Actions available to the policyholder (e.g., file a claim, cancel) |
| Modals | Informational overlays triggered by user actions |
| Conditional rules | Visibility logic that shows or hides elements based on policy state |
Vertical Data Configurator
Section titled “Vertical Data Configurator”Manages product-level metadata that is reused across the app:
| Building block | Examples |
|---|---|
| Visual assets | Color codes, icons, illustrations |
| Descriptions | Product names, taglines, plan names |
| Policy modals | Content for policy-related informational overlays |
| Regions | Geographic availability configuration |
How Data Flows
Section titled “How Data Flows”flowchart LR
A["<b>Strapi CMS</b><br/>Content editors define<br/>questionnaires, rules,<br/>quote pages, verticals"] -->|REST API| B["<b>Sync Scripts</b><br/>CLI tools fetch data<br/>and generate TypeScript"]
B -->|Generated .ts files<br/>committed to repo| C["<b>React App</b><br/>Imports questionnaire<br/>definitions at build time"]
C -->|Passes definition to| D["<b>qnr-framework</b><br/>Renders multi-step flow<br/>with rules & state"]
D -->|Interactive UI| E["<b>User's Browser</b><br/>End user answers<br/>questions & signs up"]
Why This Architecture?
Section titled “Why This Architecture?”Separation of concerns
Section titled “Separation of concerns”Product teams own the content (Strapi). Engineers own the rendering logic (qnr-framework and app code). The sync pipeline is the controlled handoff point between the two.
Code review as a safety net
Section titled “Code review as a safety net”Because synced files are committed to the repo, every questionnaire change goes through a pull request. This catches issues like broken rules, missing translations, or incorrect question IDs before they reach users.
Performance
Section titled “Performance”Questionnaire definitions are bundled into the app at build time — there is no runtime API call to load a questionnaire. This means instant rendering with no loading spinners.
Flexibility
Section titled “Flexibility”The CMS handles the common case (standard question types, rules, screens), while the app codebase handles anything that needs custom behavior. You can mix CMS-defined questions with hand-coded components in the same flow.
Where Things Live
Section titled “Where Things Live”| What | Where |
|---|---|
| Questionnaire definitions (CMS) | Strapi admin → Content Manager → Questionnaire |
| Generated questionnaire files | apps/app/src/syncedData/ |
| Sync scripts | apps/app/src/syncedData/scripts/ |
| qnr-framework source | @getpopsure/qnr-framework package |
| Custom app components | apps/app/src/features/ |
| Vertical definitions (CMS) | Strapi admin → Content Manager → Vertical |
| Quote page definitions (CMS) | Strapi admin → Content Manager → Quote-Page |
See also
Section titled “See also”- Guiding Principles — when to use the CMS, when to use code, and how to keep the system maintainable
- Strapi Content Types Reference — overview of all 28+ content types in the CMS
- Sync Commands Reference — CLI reference for the sync pipeline
- Syncing and Previewing — hands-on tutorial covering the full sync workflow