FhirQuestionnaireWidget 1.0.16
dotnet add package FhirQuestionnaireWidget --version 1.0.16
NuGet\Install-Package FhirQuestionnaireWidget -Version 1.0.16
<PackageReference Include="FhirQuestionnaireWidget" Version="1.0.16" />
<PackageVersion Include="FhirQuestionnaireWidget" Version="1.0.16" />
<PackageReference Include="FhirQuestionnaireWidget" />
paket add FhirQuestionnaireWidget --version 1.0.16
#r "nuget: FhirQuestionnaireWidget, 1.0.16"
#:package FhirQuestionnaireWidget@1.0.16
#addin nuget:?package=FhirQuestionnaireWidget&version=1.0.16
#tool nuget:?package=FhirQuestionnaireWidget&version=1.0.16
QuestionnaireRenderer — embeddable FHIR form widget
The right-hand runtime of the FHIR Questionnaire Builder, packaged as a
self-contained, embeddable widget. Drop a FHIR R4 Questionnaire into any web
page and get a live, fillable form that runs the SDC logic (enableWhen,
calculatedExpression, constraints, validation) and returns a valid
QuestionnaireResponse — no builder shell, no iframe, no framework.
- In-page & multi-instance — put several forms on one page; each keeps its own answers, calculations and validation state with zero cross-talk.
- Vanilla ES module — no React/Vue/Angular required. Works with any stack.
- Host-driven UI — the widget has no menus; you turn features on through
configand drive it through a small public API.
Table of contents
- Install
- Quick start
- Configuration
- Public API
- Events
- Example: custom “Export FHIR R4” button
- Preview modes
- Multiple isolated widgets
- Styling
- Browser support & dependencies
Install
Fastest: CDN (no install, no auth)
Load the widget straight from jsDelivr, pinned to a release tag — nothing to download, no npm, no token:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/sergeymosyakov/fhir-questionnaire-builder@widget-v1.0.3/dist/questionnaire-widget.css">
<script type="module">
import { QuestionnaireRenderer } from 'https://cdn.jsdelivr.net/gh/sergeymosyakov/fhir-questionnaire-builder@widget-v1.0.3/dist/questionnaire-widget.js';
// …
</script>
Or the classic global build (window.FhirQuestionnaireWidget):
<script src="https://cdn.jsdelivr.net/gh/sergeymosyakov/fhir-questionnaire-builder@widget-v1.0.3/dist/questionnaire-widget.global.js"></script>
Pin to a specific
@widget-v1.0.xtag for stability; jsDelivr caches immutably.
Download the release bundle
Grab the files from the GitHub Releases page and self-host them. Each release contains:
| File | Use it when… |
|---|---|
questionnaire-widget.js |
You use ES modules (import). Recommended. |
questionnaire-widget.global.js |
You want a classic <script> global (window.FhirQuestionnaireWidget). |
questionnaire-widget.css |
Always — the widget’s styles. |
Verify the download with the SHA256SUMS.txt published alongside the assets.
<link rel="stylesheet" href="questionnaire-widget.css">
<script type="module">
import { QuestionnaireRenderer } from './questionnaire-widget.js';
// …
</script>
npm (optional, for bundler projects)
If a maintainer has published to npm, bundler users (React / Vite / webpack) can:
npm install @sergeymosyakov/questionnaire-widget
import { QuestionnaireRenderer } from '@sergeymosyakov/questionnaire-widget';
import '@sergeymosyakov/questionnaire-widget/css';
npm publishing uses Trusted Publishing (OIDC from GitHub Actions — no token/secret involved); the CDN and Release above need no install and no auth and remain the primary channels.
Or build it yourself from source:
npm install
npm run build:widget # → dist/questionnaire-widget.{js,global.js,css}
NuGet (for .NET web apps)
For Blazor, Razor Pages, or MVC apps that want the widget's static JS/CSS assets without touching npm:
dotnet add package FhirQuestionnaireWidget
Reference it from a Razor/HTML page — the assets are served under
_content/FhirQuestionnaireWidget/:
<link rel="stylesheet" href="_content/FhirQuestionnaireWidget/questionnaire-widget.css">
<script type="module">
import { QuestionnaireRenderer } from '/_content/FhirQuestionnaireWidget/questionnaire-widget.js';
// …
</script>
Same publishing model as npm above — NuGet Trusted Publishing (OIDC from GitHub Actions, no token/secret). It's a static-asset-only package — no C# API surface, just the built widget files.
Quick start
<link rel="stylesheet" href="questionnaire-widget.css">
<div id="form"></div>
<script type="module">
import { QuestionnaireRenderer } from './questionnaire-widget.js';
const questionnaire = await (await fetch('my-questionnaire.json')).json();
const widget = new QuestionnaireRenderer(document.getElementById('form'), {
questionnaire,
config: { previewMode: 'patient', validation: true },
});
widget.on('response-changed', qr => console.log('answers now:', qr));
</script>
That renders the questionnaire as a patient-facing form with a live PASS/FAIL validation badge.
Configuration
new QuestionnaireRenderer(mountEl, { questionnaire, response?, config? })
| Argument | Type | Description |
|---|---|---|
mountEl |
HTMLElement |
The container the form is rendered into (its contents are replaced). |
questionnaire |
object |
FHIR R4 Questionnaire JSON. Required. |
response |
object |
Optional QuestionnaireResponse to pre-fill answers. |
config |
object |
Options below. Everything is off unless you opt in. |
config options
| Option | Type | Default | What it does |
|---|---|---|---|
previewMode |
'patient' \| 'preview' \| 'json' |
'patient' |
Form view (see Preview modes). |
search |
boolean |
false |
A search box that highlights matching rows (or JSON). |
validation |
boolean |
false |
Live PASS / FAIL badge + dropdown of items still needing attention; clicking one scrolls to it. |
explain |
boolean |
false |
Makes calculated values and FHIRPath/enableWhen conditions clickable to open an Explain popup showing why a value or visibility rule evaluates the way it does. |
tooltips |
boolean |
false |
Rich hover tooltips describing each field and its FHIR mapping. |
navButton |
boolean |
false |
A “go to builder node” arrow on each row (only meaningful when a builder is present). |
viewPrefs |
object |
{} |
Design-view toggles: { showLinkId, showPrefix, showBadges, showHiddenItems }. |
language |
string |
'' |
Show a translated language if the questionnaire carries translations ('' = source). |
fhirBaseUrl |
string |
— | FHIR base server for reference search, server-side $populate, and populate()/structureMapPopulate() below. |
getAuthToken |
() => string \| Promise<string> |
— | Returns a Bearer token for fhirBaseUrl requests (your host's own auth — e.g. you're already logged into the FHIR server in your own app). The widget never runs its own OAuth login popup; without this, requests go out with no Authorization header (fine for public/CORS-open test servers). Independent per instance. |
corsProxy |
string |
— | CORS proxy for the FHIR/terminology requests. |
terminology |
object |
— | This widget's own terminology server for external answerValueSet expansion: { server, corsProxy, nlmApiBase }. Independent per instance — two widgets on the same page can point to two different terminology servers. Falls back to the public HL7 server (tx.fhir.org/r4) when omitted. |
readOnly |
boolean |
false |
Render answers without editable controls. |
onProgress |
(msg\|null) => void |
— | Called with a message while long operations run, null when done. |
Public API
widget.getResponse(); // → current answers as a FHIR QuestionnaireResponse
widget.setResponse(qr); // load answers from a QuestionnaireResponse
widget.setLanguage('es'); // switch active language ('' = source)
widget.setConfig({ language: 'es' }); // runtime config — only `language` takes effect
widget.populate('Patient/123'); // SDC $populate against config.fhirBaseUrl
widget.structureMapPopulate('Patient/123'); // run the Questionnaire's sourceStructureMap against a fetched resource
widget.on(event, cb); // subscribe (returns the widget)
widget.off(event, cb); // unsubscribe
widget.destroy(); // remove the widget and free all listeners
populate()/structureMapPopulate() merge the resulting answers in place and
emit info on success or error on failure (see Events) — nothing
is shown on the page unless you subscribe.
getResponse() always returns a fresh, valid FHIR R4 QuestionnaireResponse
built from the current answers — this is your integration point for saving,
submitting, or exporting.
Note: the chrome flags (
search,validation,explain,tooltips,navButton) are construction-time — set them in the initialconfig.setConfig()only applieslanguageat runtime; to change chrome,destroy()and create a new instance.
Events
Subscribe with widget.on(name, cb):
| Event | Payload | Fires when |
|---|---|---|
ready |
— | The widget has mounted and rendered the first time. |
response-changed |
QuestionnaireResponse |
Any answer changes. |
language-changed |
string (lang) |
The active language changes. |
render |
— | The form re-renders. |
info |
string |
populate()/structureMapPopulate() succeeded. |
error |
string |
populate()/structureMapPopulate() failed (e.g. fhirBaseUrl not set) — nothing is shown unless you listen. |
Example: custom “Export FHIR R4” button
The widget deliberately ships no toolbar. To let a user export their answers,
add your own button on the host page and call getResponse() — the returned
object is a ready-to-save FHIR R4 QuestionnaireResponse:
<div id="form"></div>
<button id="export">Export FHIR R4</button>
<script type="module">
import { QuestionnaireRenderer } from './questionnaire-widget.js';
const questionnaire = await (await fetch('my-questionnaire.json')).json();
const widget = new QuestionnaireRenderer(document.getElementById('form'), {
questionnaire,
config: { previewMode: 'patient', validation: true },
});
document.getElementById('export').addEventListener('click', () => {
const qr = widget.getResponse(); // ← ask the widget for answers
const blob = new Blob([JSON.stringify(qr, null, 2)], { type: 'application/fhir+json' });
const url = URL.createObjectURL(blob);
const a = Object.assign(document.createElement('a'), {
href: url, download: 'questionnaire-response.json',
});
a.click();
URL.revokeObjectURL(url);
});
</script>
A working version of this button is in the live demo.
Preview modes
config.previewMode picks how the form looks:
patient— the clean, fillable form a respondent sees.preview— the design view with link IDs, prefixes and status badges.json— the liveQuestionnaireResponse/QuestionnaireJSON.
Multiple isolated widgets
Each widget runs on its own event channel and its own answer store, so two forms on the same page never affect each other:
const a = new QuestionnaireRenderer(elA, { questionnaire: qA });
const b = new QuestionnaireRenderer(elB, { questionnaire: qB });
// answering a never touches b
See the live demo for a three-widget page (one per preview mode) over the same questionnaire.
Styling
Link questionnaire-widget.css once. It carries the widget’s design tokens and
all preview/control/modal styles, scoped so they don’t leak into your page’s
layout (no global body/reset rules). Override the CSS custom properties on a
wrapping element to re-theme (e.g. --c-primary, --c-border, --c-surface).
Browser support & dependencies
- Modern evergreen browsers (ES2020 modules).
- The ESM/global bundles include their runtime dependencies (FHIRPath, DOMPurify,
marked) — no extra
<script>tags needed. - No network calls unless you set
fhirBaseUrl(reference search /$populate) or use terminology expansion.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.