Skip to content

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

Terminal window
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
PackagePurpose
prosemirror-*Core ProseMirror runtime, keymaps, history, cursors, menu bar, lists, tables
@citolab/prose-qtiQTI descriptors, node specs, plugins, item roundtrip
@citolab/prose-extensionsGeneric 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.

SubpathInteraction 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/sharedShared building blocks (qti-prompt, qti-simple-choice, …)

Each interaction subpath exports:

  • *InteractionDescriptortagName, insertCommand, enterCommand, backspaceCommand, pluginFactories, decoratorPluginFactories, attributePanelMetadata, nodeSpecs
  • qti*InteractionNodeSpec — ProseMirror node spec (also reachable via descriptor.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

PackageWhat it providesWhen to use this
prosemirror-*Core ProseMirror runtimeAlways — QTI Editor is built on ProseMirror.
@citolab/prose-qtiDescriptors, interactions, node specs, plugins, item roundtripThe main runtime.
@citolab/prose-extensionsGeneric ProseMirror pluginsWhen you want block selection, node attribute sync, semantic paste, etc.
@citolab/prose-qti/schemaqtiBasicNodes/qtiBasicMarks base schema nodesWhen you assemble a raw ProseMirror schema by hand and want image dimensions to survive QTI XML roundtrip.
@citolab/prose-qti/qti3-item-importStandard QTI 3.0 item normalizationWhen you need to ingest third-party QTI XML before parsing into the editor.
@citolab/prose-qti/item-roundtripOne-call import/export between ProseMirror and QTI item XMLWhen you want a single helper for the full QTI item roundtrip.
@citolab/prose-qti/qti-prose.cssBundled @qti-components/theme + core-css.css stylesheetWhen you want one stylesheet import instead of pairing two yourself.
@citolab/prose-qti/decorations.cssStyles for the opt-in editor decorators (hover boundary, add/remove buttons, settings pill)When you also install decoratorPluginFactories for an interaction.
@citolab/prose-qti/transformersRe-export of @qti-components/transformersWhen you call qtiTransformItem/qtiTransformTest and don’t want to declare @qti-components/transformers separately.