Installation
QTI Editor is split into focused packages so you only install what you need. The qti-example-editor boilerplate is the reference shape; this page lists the packages it uses and how to install them.
Install
pnpm add prosemirror-state prosemirror-view prosemirror-model prosemirror-commands \ prosemirror-keymap prosemirror-history prosemirror-dropcursor prosemirror-gapcursor \ prosemirror-menu prosemirror-schema-basic prosemirror-schema-list prosemirror-tables \ @citolab/prose-qti @citolab/prose-extensions| Package | Purpose |
|---|---|
prosemirror-* | Core ProseMirror runtime, keymaps, history, cursors, menu bar, lists, tables |
@citolab/prose-qti | QTI descriptors, node specs, plugins, item roundtrip |
@citolab/prose-extensions | Generic ProseMirror plugins (block select, node attrs sync, semantic paste, …) |
@citolab/prose-qti treats the ProseMirror packages as peer dependencies. Keep the ProseMirror packages on one aligned release set and upgrade them together; reuse versions your app already has rather than introducing a second copy.
Build your base schema nodes/marks from qtiBasicNodes/qtiBasicMarks (@citolab/prose-qti, also reachable via the @citolab/prose-qti/schema subpath) instead of importing prosemirror-schema-basic directly — see QTI Base Schema. prosemirror-schema-basic stays a dependency either way; qtiBasicNodes is built on top of it and only patches the image node.
Stylesheet
Import @citolab/prose-qti/qti-prose.css wherever your app renders the editor’s document — it is a mandatory plain-CSS stylesheet (no Tailwind or PostCSS plugins required) that bundles the upstream @qti-components/theme stylesheet together with this package’s own interaction element backgrounds, spacing, and rubric-block boundary styles, in the order the cascade requires:
@import '@citolab/prose-qti/qti-prose.css';This is the only stylesheet import a host needs — @qti-components/theme is a dependency of @citolab/prose-qti itself, so you never declare it or track which qti-components build the editor is pinned to. Add a brand overlay stylesheet after this import, not before.
If you’d rather manage the two stylesheets yourself (e.g. to share @qti-components/theme across other UI in your app), import them separately and in this order — reversing it silently breaks the CSS cascade:
@import '@qti-components/theme/item.css';@import '@citolab/prose-qti/core-css.css';See apps/qti-example-editor/src/app.css for the reference import order.
Interactions that ship an editor decorator (currently choice — see Choice Interaction) need a second, opt-in stylesheet on top of the mandatory one above:
@import '@citolab/prose-qti/decorations.css';QTI interactions
Each interaction has its own subpath. Import the descriptor and node spec explicitly per interaction — see the TypeScript Integration guide for the full shape.
| Subpath | Interaction type |
|---|---|
@citolab/prose-qti/components/choice | <qti-choice-interaction> |
@citolab/prose-qti/components/extended-text | <qti-extended-text-interaction> |
@citolab/prose-qti/components/text-entry | <qti-text-entry-interaction> |
@citolab/prose-qti/components/inline-choice | <qti-inline-choice-interaction> |
@citolab/prose-qti/components/hottext | <qti-hottext-interaction> |
@citolab/prose-qti/components/order | <qti-order-interaction> |
@citolab/prose-qti/components/match | <qti-match-interaction> |
@citolab/prose-qti/components/gap-match | <qti-gap-match-interaction> |
@citolab/prose-qti/components/select-point | <qti-select-point-interaction> |
@citolab/prose-qti/components/rubric-block | <qti-rubric-block> |
@citolab/prose-qti/components/shared | Shared building blocks (qti-prompt, qti-simple-choice, …) |
Each interaction subpath exports:
*InteractionDescriptor—tagName,insertCommand,enterCommand,backspaceCommand,pluginFactories,decoratorPluginFactories,attributePanelMetadata,nodeSpecsqti*InteractionNodeSpec— ProseMirror node spec (also reachable viadescriptor.nodeSpecs)register.js— side-effect module that defines the interaction’s Lit custom element
Custom elements are registered through side-effect imports per interaction (@citolab/prose-qti/components/<name>/register.js) plus the shared elements you use (@citolab/prose-qti/components/shared/components/qti-simple-choice/register.js, …).
Item roundtrip
@citolab/prose-qti/item-roundtrip exposes the import/export bridge:
import { importItemFromUrl, exportItemXml } from '@citolab/prose-qti/item-roundtrip';
const doc = await importItemFromUrl(href, schema);const xml = exportItemXml(view.state.doc, schema);See the QTI Item Roundtrip reference for the full surface.
Transformers
@citolab/prose-qti/transformers re-exports @qti-components/transformers in full (qtiTransformItem, qtiTransformTest, …) so a host never names @qti-components/transformers itself:
import { qtiTransformTest } from '@citolab/prose-qti/transformers';Like the stylesheet above, this exists so a consuming app tracks only @citolab/prose-qti’s version — not a second, separately-pinned qti-components package that has to be bumped in lockstep.
Package overview
| Package | What it provides | When to use this |
|---|---|---|
prosemirror-* | Core ProseMirror runtime | Always — QTI Editor is built on ProseMirror. |
@citolab/prose-qti | Descriptors, interactions, node specs, plugins, item roundtrip | The main runtime. |
@citolab/prose-extensions | Generic ProseMirror plugins | When you want block selection, node attribute sync, semantic paste, etc. |
@citolab/prose-qti/schema | qtiBasicNodes/qtiBasicMarks base schema nodes | When you assemble a raw ProseMirror schema by hand and want image dimensions to survive QTI XML roundtrip. |
@citolab/prose-qti/qti3-item-import | Standard QTI 3.0 item normalization | When you need to ingest third-party QTI XML before parsing into the editor. |
@citolab/prose-qti/item-roundtrip | One-call import/export between ProseMirror and QTI item XML | When you want a single helper for the full QTI item roundtrip. |
@citolab/prose-qti/qti-prose.css | Bundled @qti-components/theme + core-css.css stylesheet | When you want one stylesheet import instead of pairing two yourself. |
@citolab/prose-qti/decorations.css | Styles for the opt-in editor decorators (hover boundary, add/remove buttons, settings pill) | When you also install decoratorPluginFactories for an interaction. |
@citolab/prose-qti/transformers | Re-export of @qti-components/transformers | When you call qtiTransformItem/qtiTransformTest and don’t want to declare @qti-components/transformers separately. |