React component library for rendering forms from HDS item definitions. Converts HDS ItemDefs to form fields using Tailwind CSS styling, and handles bidirectional data conversion between form values and Pryv events.
Renders a single form field based on an HDS item data definition.
import { HDSFormField } from 'hds-forms';
<HDSFormField
itemData={itemDef.data}
value={value}
onChange={(v) => setValue(v)}
/>Supported field types: checkbox, date, text, number, select, composite, datasource-search.
When an item declares a dateTime or duration block, <HDSFormField> renders the corresponding companion input next to the value field — the companions write to the surrounding Pryv event's event.time / event.duration (Pryv-native), not to the payload schema. Companions can be exposed individually:
import { EventTimeInput, EventDurationInput } from 'hds-forms';
<EventTimeInput value={time} onChange={setTime} mandatory={false} />
<EventDurationInput value={duration} onChange={setDuration} maxSeconds={315360000} />EventDurationInput has four modes — No (point-in-time), Ongoing (open-ended), Length (numeric + unit), End time (computed) — and respects mandatory / allowNull / maxSeconds from the item definition.
Renders a full form section (multiple fields + submit button). Resolves item keys via getHDSModel().
import { HDSFormSection } from 'hds-forms';
// Permanent section (default) — one-time profile data
<HDSFormSection
section={{ type: 'permanent', itemKeys: ['bloodType', 'allergies'] }}
onSubmit={(formData) => console.log(formData)}
/>
// Recurring section — repeated entries with date picker
<HDSFormSection
section={{ type: 'recurring', label: { en: 'Daily tracking' }, itemKeys: ['temperature', 'notes'] }}
onSubmit={(formData) => console.log(formData)}
entries={previousEntries}
onEditEntry={(index) => handleEdit(index)}
onDeleteEntry={(index) => handleDelete(index)}
/>Recurring sections render a date picker (defaults to today), an "Add entry" button, and a list of previously submitted entries with edit/delete actions.
Sections accept optional customFieldKeys: string[] + customFields: CustomFieldDeclaration[]. Each custom-field key is rendered alongside canonical items via the existing <HDSFormField>. Form values for custom fields live under __cf::{templateId}::{key} so they can't collide with canonical itemKeys.
<HDSFormSection
section={{
type: 'recurring',
itemKeys: ['fertility-cycles-start'],
customFieldKeys: ['dcom'],
customFields: appTemplate.customFields // from hds-lib's loadTemplate()
}}
onSubmit={...}
/>For the submit / prefill round-trip, use buildCustomFieldEntries(customFieldKeys, customFields) to produce { key, itemDef }[] entries that plug straight into formDataToActions() and prefillFromEvents() alongside canonical entries. See hds-lib's CUSTOM-FIELDS-AND-SYSTEM.md for the full design reference.
Sections can pin an item to a descendant stream via itemCustomizations[itemKey].context:
<HDSFormSection
section={{
type: 'recurring',
itemKeys: ['procedure-coded'],
itemCustomizations: {
'procedure-coded': { context: 'procedure-fertility' }
}
}}
onSubmit={...}
/>The resulting event uses the descendant streamId (procedure-fertility) instead of the item's default parent (procedure). forEvent() walks back up to resolve the original itemDef. Cross-subtree contexts are rejected. See data-model documentation/TREATMENT-PROCEDURE.md for the design rationale.
Renders a typeahead search field bound to a remote dataset endpoint (e.g. datasets-service's /medication, /treatment, /procedure). On selection, populates the host item's payload (drug / regimen / procedure) and any companion fields (intake.{doseValue, doseUnit, route}, procedure findings[], free-text notes). Companion fields render inline.
Displays a compact table of recurring entries. Used internally by HDSFormSection but also exported for custom layouts.
import { schemaFor, jsonFormForItemDef } from 'hds-forms';
// Generate JSON Schema from item data
const schema = schemaFor(itemDef.data);
// Full conversion pipeline: schema + event data converter
const { schema, eventDataForFormData } = jsonFormForItemDef(itemDef);
const eventData = eventDataForFormData(formValues);import { prefillFromEvents, formDataToEventBatch } from 'hds-forms';
// Pre-fill form from existing Pryv events
const values = prefillFromEvents(itemDefs, events);
// Convert form submission to Pryv event batch
const batch = formDataToEventBatch(itemDefs, formData, timestamp);An interactive test application is included in src-test-app/. It provides a UI to exercise all components and field types with the real HDS model.
npm run test-app:setup # install test app dependencies (one-time)
npm run test-app # launch dev server
npm run build:app # build test app into dist/ for gh-pages- Node.js >= 20
- npm
hds-lib(HDS model must be initialized before using components)
npm run setupnpm run dev # library watch mode
npm run build # library build (outputs to js/)
npm run test # run unit tests
npm run test:watch # run tests in watch mode
npm run lint # check linting
npm run typecheck # check types