Appearance
Building a Custom Engine
An engine is a manifest that names a core version and a set of question components, built into one self-contained HTML file. You build a custom engine when you need question types Medipal does not ship, a different look and feel for a programme, or behaviour tailored to a device or population.
There are two ways to build one, and they share the same manifest format:
| Method | Best for |
|---|---|
| Engine Editor in the admin web app | Editing components in the browser with a live preview, and publishing to the instance's engine store with one click. See Engine Editor. |
| Engine builder tool (command line) | Working in your own repository with your own editor, tests and CI; building bundles for hosting elsewhere. |
Prerequisites
- A current Node.js LTS runtime, for the builder tool.
- Access to a published questionnaire core (the manifest of the core you are building against). Cores are listed in the Engine Editor and registered on folders.
- Familiarity with Vue 3 single-file components. Question components are Vue components.
- A place to host the result: the instance's engine store (via the Engine Editor) or any static file host you control.
The engine manifest
The manifest is the single source of truth for an engine. It carries the metadata, the core reference and the full source of every component and shared file as strings, which is what lets the web app edit and rebuild an engine without a repository.
json
{
"id": "clinic-engine",
"name": "Clinic Engine",
"icon": "lucide:stethoscope",
"description": "Question set for the outpatient PROM programme",
"version": "1.0.0",
"core": {
"id": "questionnaire-core",
"version": "<core version>",
"baseUrl": "<where the core is published>",
"packageName": "questionnaire-core-<core version>",
"main": "<core script file>",
"style": "<core stylesheet file>",
"files": ["<core script file>", "<core stylesheet file>"]
},
"components": [
{
"name": "numeric",
"label": "Numeric",
"icon": "lucide:hash",
"mainFile": "NumericRenderer",
"files": {
"NumericRenderer.vue": "…component source…",
"Schema.json": "…settings schema…",
"UISchema.json": "…settings form layout…"
}
}
],
"sharedFiles": {
"BaseAnswer.vue": "…",
"i18n.ts": "…"
}
}| Field | Purpose |
|---|---|
id, version | Together they form the package name of the built bundle. Increase the version for every published change; hosts and questionnaires bind to a specific version. |
name, icon, description | Shown in the Engine Editor list and on the questionnaire's configuration page. |
core | Which core to build against. Copy this block from the core's published manifest; do not hand-edit individual entries. |
components | One entry per question type. name is the question type identifier used in questionnaires; label and icon appear in the designer's question palette; mainFile is the component file (without extension). |
components[].files | The component's source files. Schema.json and UISchema.json describe the type's settings form in the designer (see Custom Question Components). |
sharedFiles | Files every component can import: base UI pieces, translation helpers, utilities. |
Built-in question types
The default engine ships these components. A custom engine can reuse them as-is, modify them or drop them.
| Type | What it renders |
|---|---|
numeric | Number input with unit, minimum/maximum, step and locale-aware formatting |
singleChoice | One option from a list |
multipleChoice | Several options from a list |
slider | Vertical slider with labelled ends and optional tooltip |
date | Calendar: single date, several dates or a range, with past/future restrictions |
text | Free text with length limits |
A page type (text only) is always available and needs no component.
Build with the builder tool
The builder generates the engine's entry files from the manifest, downloads the referenced core, and bundles everything into a single HTML file.
- Create a project from the engine builder template and place your manifest in it.
- Generate the sources from the manifest. This downloads the core and writes the component files to disk. Repeat after every manifest change.
- Run the dev server. Two pages are served: the engine itself (blank until a host initialises it) and a preview harness that loads the engine in an iframe, lets you paste a questionnaire, choose the language and sections, toggle a phone-sized frame, and shows the submission payload as it comes back.
- Build the bundle. The output is a folder named after
idandversioncontainingindex.htmland a generatedmanifest.jsonthat adds the question type definitions (name, label, icon, settings schema and form layout) read by the designer. - Publish the folder to the location referenced by your engine's base URL, keeping the folder name unchanged.
Helper commands exist to switch the engine to a newer core (fetch a core manifest and patch the core block) and to load an existing engine manifest from a URL so you can fork a published engine.
Register and use the engine
- Open the folder in which the engine should be available and add the engine's URL (the folder containing
manifest.json) to the folder's list of questionnaire engines. Nested folders inherit the list; see Folders & Configuration. - Create a questionnaire in that folder; the create dialog offers the folder's engines.
- For an existing questionnaire, open its configuration and choose Change Engine, entering the engine URL. The designer palette updates to the engine's question types.
- Publish the questionnaire. Its definition now carries the engine identity, and every host loads that bundle.
Upgrading the core
- Fetch the new core's manifest and replace the
coreblock of your engine manifest (the builder has a command for this). - Regenerate, run the preview, and check each component against the core's changelog, which is included in the core manifest.
- Increase the engine version, build and publish.
- Move questionnaires to the new engine version from their configuration page when you are ready; already published questionnaires keep the version they were published with.
Checklist before publishing
- Every question type used by your questionnaires has a component in the manifest.
- Each component has a
Schema.jsonandUISchema.json, or the type has no settings. - All translations you rely on exist for every language the folder allows.
- The bundle renders correctly in the preview harness in both colour modes and at 120% zoom.
- Validation blocks Next for required questions and clears when fixed.
- The submission payload contains a
valueandvalue_typefor every answer. - The version number is new.