How to review H5P content types?

When someone programs an H5P content type, it is common practice that someone else gives it a review. That means someone checks whether everything is working as intended. Moreover, one takes a look if there’s something that violates widely recognized standards, if there’s something that does not follow custom policies, or if something within the source code could be improved.

The whole process typically consists of many different aspects and tasks to do. Therefore it feels like a good idea to have some guideline that hints to H5P specific topics. A checklist to document findings and to be able to communicate them back to the developer is also useful. That is what this post is supposed to provide.

The post is no ultimate guide. It will definitely not cover all the rather general coding aspects that are not related to H5P in particular. And it will probably evolve over time as it certainly is incomplete. And every developer will have her/his own opinions about how certain things should be done. For instance, this post is built on the premise that there are many rather inexperienced developers out there who would like to learn coding for H5P but get confused easily by different frameworks, tool chains or approaches. Some  more experienced people will not like certain recommendations that stem from that premise. That’s likely and fine! This post aims at providing you with a good basis for creating your own tool helping you with reviews, so you most definitely will want to and need to adjust it to your needs!

There are different aspects of H5P code (for content types) that one should check. Not all of them may be relevant depending on what one is looking at, and the aspects listed here overlap a little. However, they represent certain things relevant to understanding H5P as well, so let’s not try to be too academic.

If you have created your own content type and want to ensure it’s ready for release or if you want to make sure an existing content type is fine for you to install on your platform – or if you just want to contribute to H5P and make sure that quality of content types is high – you will want to check:

  • Function: Does the code do what it is supposed to do?
  • Accessibility: Can the program be used by people with disabilities?
  • Responsiveness: Does the program work on different screen sizes and devices?
  • Cross-browser: Does the code run on every major browser?
  • Cross-platform functionality: Does the content run on different H5P integrations?
  • Translations: Are translations handled correctly?
  • Security: Is the program free of obvious security issues?
  • Code: Does the code follow the H5P coding guidelines and seems intelligible?
  • Repository: Does the code repository follow good practices and follow the H5P specification?

For all those you are going to find a section including some general background information, some extra information dedicated to H5P specifics without covering all the details (that’s stuff for separate posts), and a checklist with multiple items.



We need to make sure that the code works as expected. Obviously 🙂 Ideally, the code is accompanied by automated tests that can guarantee functionality for defined test cases. One tool to do this could be Jest, for instance. But there may be no automated tests at all. test cases also often do not cover every possible issue. That’s why manual testing can be required or is advisable at least.

Normally, there is a set of test cases consisting of user stories that describe what a user is supposed to be doing, potentially a set of instructions on how to achieve that, and a description of what the expected outcome is. For instance, a very simple combo of those three things could be:

  • User story: “As a learner, I want to be able to toggle audio on and off in order to set/remove distraction.”
  • Steps: Click on the button with the note symbol.
  • Expected behavior: If the audio was turned on, it is now switched off.

Similar to automated testing, user stories may not always cover everything – because users may try things that one had not anticipated or the devil may be in conflicting settings details. That’s why one should also try to do things that are not covered by the test cases – that also means one could create a new test case … One can, of course, also just create some test content and see if everything feels okay, but that’s less thorough and prone to missing things, of course.

Essentially, “all” that needs to be done is test all the different content settings you can apply in different combinations 😀

Extra information

There are a couple of H5P specific features that may be implemented by a content type. Those may need some dedicated checking.

Resume functionality

H5P offers a resume feature that content types can support. H5P core can store the current state and return it back when the user revisits H5P content. In turn, the user can continue where he/she left off.

Question type contract

H5P content types that are of some “question type”, e.g. Multiple Choice, Dictation, etc. should implement the Question type contract. Implementing that contract ensures that other content types can use the content type as subcontent and know that certain functionality will be present in order to get certain values like scores or to reset a content type to its initial state.

The Question type contract features can be checked by running the H5P content, setting the browser’s development console’s context to the H5P iframe and then checking what the respective calls return. For instance,


should return the current user score (no matter if the user sees it already or not).


H5P content types can use the Experience Application Programming Interface (xAPI) in order to keep track of the users’ experience. It is most often used for reporting scores only, but could do much more.

In order to check xAPI functionality, one should check that xAPI statements are emitted as required, contain the required information and are valid.

  • One can check xAPI statements that are fired by H5P in the browsers’ development console by listening for them:
    H5P.externalDispatcher.on('xAPI', (event) => {
  • The xAPI specification holds general information about what information an xAPI statement should hold.
  • The validity of an xAPI statement can for instance be checked via

Since xAPI statements are used to report answers, grades, etc. to the H5P integration and thus to the platform that is hosting H5P, one should check whether the relevant values and up on that host platform. One could, for instance, use Moodle’s completion tracking and Moodle’s gradebook as a reference for determining whether “scoring” works fine. The getXAPIData function as part of the “question type contract” is relevant here.

Upgrading existing contents

Sometimes the data structure of the parameters as defined in the semantics.json file changes in a way that requires changing the configuration of existing content. That is e.g. necessary if a property is moved within the JSON hierarchy – the existing value needs to be copied to the new place and the old property can be deleted.

H5P provides a mechanism to do this. A content type can provide an upgrades.js file that can implement the required function to upgrade existing content to a later version. One needs to check if that works as expected.


  • All automated tests (if available) pass without errors.
  • All user stories (if available) lead to the expected behavior.
  • Creating different contents with different settings that are not covered by user stories seems to work fine.
  • “Weird input” does not cause issues
    • When the content is saved without entering anything but the title, the content does not crash but sets default values or informs the author about missing data.
    • When a text input field contains very much text, the content type makes sense of it, so it is not cut off and it doesn’t overflow an element, etc.
    • There are no HTML encoding errors: e.g. when an apostrophe is entered in a text field, it rendered as an apostrophe in the content type view and not as '
  • The content is reset properly and then behaves normally (if the content can be reset).
  • The user state is restored as intended (if the resume feature of H5P should be supported).
  • The xAPI statements that the content type may emit are valid.
  • The Question type contract is implemented properly (if implemented at all).
  • Existing content with different parameters gets upgraded properly by upgrades.js (if required).



Part of the mission of H5P is to enable everyone to use interactive content on the web. That includes people with certain disabilities across different devices. In turn, one needs to e.g. ensure

  • sufficient color contrast,
  • a proper size of elements on mobile devices in particular,
  • the option to use the content with the keyboard entirely,
  • proper output using screen readers,
  • offering text based alternatives for people with impaired hearing,

In order to ensure all this, H5P content should follow the WCAG 2.1 standard. WCAG has different levels for describing achievement and content types should be AA compliant.

There are numerous things one needs to check, and this document is not going to cover them in detail – and the task is two-fold. There are things that one should check by testing the view, and some things need to be checked by looking at the code. For the former, it is required to have a screen reader running that people with visual impairments can use.

Certain things could be automated when using axe for instance (locally or as github action) or some other tools, but that would be a separate chapter 🙂

Extra information

Accessibility has gained importance and there are plenty of tools that one can use to check a page or interactive content. One can, for instance, use “Lighthouse” which is part of Chrome’s development tools. Lighthouse features an accessibility check among other things and can point out potential issues. One could also use the Siteimprove Accessibility Checker plugin for Chrome that has been designed for checking accessibility in particular.

For color contrast, it is important to check CSS pseudo-elements separately, as the aforementioned tools do not check the contrast of those. One can use the online Color Contrast Checker in that case and enter the respective color values for the foreground and the background.

If you want to check an H5P content type with tools, you need to be aware that H5P runs inside an iframe. If you scan the page that the H5P content is included in, it is possible that you scan the page only, but not the H5P content. Furthermore, even if the H5P content was scanned, you’d also get findings on the page that do not relate to the H5P content. The solution here is to open the H5P content using its embed link in a separate window. This may cause some resize issues, but the checker tools can access the H5P content and the H5P content only.

Screen readers

There are a couple of different screen readers for different operating systems and browsers that may behave a little differently. For instance, there are

Ideally, more than one screen reader is checked to rule out issues that one may have in particular.


There are common elements that a web page can offer to interact with, e.g. a breadcrumb navigation, a modal dialog or a toolbar. There are patterns related to the design of ARIA support for creating those elements, and one should use those patterns. This does ensure that the elements follow common practices which for instance all screen readers should follow, too.


  • The content type can be used with a keyboard only.
    • There are no “traps” that one cannot navigate out with the keyboard.
    • Elements have a focus indicator when they are in focus.
  • The content type gives proper instructions and information when using a screen reader.
  • All elements have proper color contrast.
  • All elements are properly sized (Apple’s guidelines, for instance, suggest tappable areas of at least 44 px by 44 px on mobile devices).
  • Correct HTML tags are used (e. g. <button> for a button element instead of a <div> with the role attribute button unless there’s a good reason for it)
  • There are no redundant attributes, e.g. setting role=”button” on a <button> element.
  • Actionable elements without text have an aria-label attribute or an aria-labelledby attribute.
  • The title attribute that can be used for tooltips is not used, because it can conflict with screen readers. If one needs tooltips, one should use a custom solution (H5P Core is supposed to get a tooltip class that one can use).
  • ARIA patterns are used if available and relevant.
  • Debatable: <h> tags should not be used in H5P content types. Content types (usually) run in iframes and it’s unknown what header level the content is rendered in, so chances are that the hierarchy chosen within the content type would not make sense.
  • Content can be scaled using the browser’s zoom function.
  • Focus is not lost, e.g. when deleting an element.



Content should be responsive and adapt to various screen sizes. This does not mean to simply scale everything down, but usually involves to change the visual layout or even to change the way that user interacts with the content type.

One can, for instance, use Chrome’s responsive mode to simulate different screen sizes. One can also use BrowserStack.


  • The full screen option works as expected (if available).
  • The content remains usable well on wide screens.
  • The content remains usable well on narrow screens.
  • The content works well both in landscape and portrait mode.
  • All relevant information can be obtained on all devices (for instance, touch devices do not support a hover effect and require some other way).
  • There’s no need to have a right mouse button to access something (which doesn’t work on mobile devices).

Cross-browser functionality


Users can be using many different kinds of browsers on different devices. Browsers unfortunately may interpret HTML and CSS differently or not support the same set of JavaScript features or APIs. While there are some technical ways to limit those kinds of problems, H5P (content types’) code should be checked on different browsers. This is true for both for, the editor and the view. However, if there is no custom editor widget involved or the whole editor form has been replaced as in Branching Scenario, the focus lies on the view.

H5P Group promises to support the latest three versions of all major browsers, so it’s advisable to check those as well at least. Given the high release frequency of Chrome, Firefox and Edge, it seems okay to only check the latest version.

The relevant code base of browsers is the same across all the desktop operating systems, so there’s no need to check this on different operating systems. Safari on MacOS is different from Safari on iOS, however. The latter often lacks certain features or implements things differently.

There’s no real need to also check Chrome or Firefox on iOS, because Apple forces other browser vendors to use their webkit render engine that is used by Safari on iOS. In consequence, Chrome and Firefox (or other browsers on iOS) are essentially Safari with some other functions.

In order to test a content type on all those different browsers and operating systems or even devices without the need to own them physically, one can use the online service BrowserStack.

Extra information

There are a couple of issues that have been the cause of trouble for cross-browser compatibility in earlier H5P development, but given that the engines are constantly developed further, this information here can be outdated. A good source for determining what feature is supported by what browser is the service “Can I use”.

Issues that have caused trouble before and should be checked if relevant for the content type:

  • The webm video format is not fully supported by Safari on iOS and only fully supported by Safari on MacOS as of version 16. (H5P.Video and content types handling using that library)
  • Safari on iOS does not preload audio files on cell based connection in order to save bandwidth which usually results in delayed playing of audio samples. One needs to use the Web Audio API and buffer sounds if this is relevant. (e.g. H5P.SingleChoiceSet, H5P.JigsawPuzzle, H5P.GameMap)
  • There is a Web Speech API with support for speech recognition that slowly seems to get traction, but users report mixed experiences. Chrome was the browser that implemented it first a long time ago, Edge seems to support it in theory, Firefox has experimental support that needs to be enabled on a special settings page, Safari seems to have support since version 14.x but also seems to require a special prefix for the SpeechRecognition API. (H5P.SpeakTheWords)
  • When using the HTML Drag and Drop API, Firefox may scale down the draggables even though this may not be intended. A workaround is to copy the draggable into an image and to use this as the draggable. (H5P.SortParagraphs)
  • The Date function of Firefox is fully adhering to the specification, but other browsers are more tolerant when interpreting the function argument and making sense of it. (H5PEditor.DateTime)
  • Chrome (on Android) always reports the actual horizontal and vertical size for screen.width and screen.height depending on how a user holds the device. Safari on iOS, however, determines those values in portrait mode and keeps them fixed. That for instance means that even if one holds the device with a screen resolution of 1920 px by 1080 px in landscape mode, screen.width will report 1080 and screen.height will report 1920. Matter of perspective … (H5P.Bingo)


  • Code runs fine on (the latest three versions of) Chrome on desktop.
  • Code runs fine on (the latest three versions of) Firefox on desktop.
  • Code runs fine on (the latest three versions of) Edge on desktop.
  • Code runs fine on the latest three versions of Safari on MacOS.
  • Code runs fine on the latest three versions of Safari on iOS.
  • Code runs fine on (the latest three versions of) Chrome on Android.

Cross-platform functionality


This aspect is rather an edge case, because H5P content types are supposed to not depend on a particular H5P integration or platforms such as Moodle or WordPress. However, the platform itself may do things that interfere with what the H5P content tries to achieve.

Unfortunately, it does not feel possible to come up with a clearly defined checklist up-front. Issues that are known for having caused problems in the past are listed here, however.


  • If both the content type and the platform manage URLs in some way (e.g. use the hashchange event or change URL fragments), there are no conflicts.



H5P is to be used by everyone, so there should be as many translations as possible. H5P has a mechanism to allow content types to be translatable.

In a nutshell: Translatable fields are defined in semantics.json and as such passed to the view with the content’s parameters. In a languages directory, there should be an .en.json file which is a stripped down version of semantics.json that only contains text phrases that need translation. That .en.json file is a template for translations into other languages, e.g. nb.json for Norwegian Bokmål or sw.json for Swahili.

There are at least two ways to check if everything is translatable. One way is to go through the code and make sure that every text string comes from the definition in semantics.json. The other way is to override the translations in the editor. And see whether that translation is used. It’s important to keep in mind that not all translatable fields are visible, but they can be used in aria-labels as well.

The validity of the translations can be checked using the H5P CLI tool with the command h5p check-translations --diff $LANGUAGE_CODE $LIBRARY_DIR

There’s also a beta github action to check H5P translations that contains a bash script that can be used to continuously check the validity of the files (including validity of the json file) or to check everything manually.

The correctness of translations cannot be checked, of course, unless one is very polyglot 🙂


  • There’s a .en.json template available inside the language folder.
  • All text phrases that are used in a content type are translatable.
  • There are no unused translation fields in semantics.json.
  • The structure of the translation files matches the structure of semantics.json (e.g. no missing fields because semantics.json was updated but not the translation files).
  • The file name of translation files does not contain upper case letters.



Security is important to make sure no one can perform malicious attacks, in particular use cross-site-scripting (XSS) to inject JavaScript code in H5P content.

For H5P content types, that essentially means to check all ways that the user can input some text in the editor. For instance, one could put JavaScript into every editor text field, e.g. <script>alert('XSS!');</script> When viewing the content type, that code should not be executed, so one should not see the respective alert pop up.

The next obvious aspect to check is checking dependencies and 3rd party code for known vulnerabilities. It’s pretty simple to check npm dependencies by cloning a repository and running npm install which then will notify about known vulnerabilities. For other 3rd party code, checking it for vulnerabilities can mean tremendous effort if taken seriously.

In theory, one should also check whether all the npm dependencies are maintained, so someone can actually fix security relevant bugs. That’s hardly possible in practice though.


  • No JavaScript code can be injected via the editor’s text fields.
  • All npm dependencies are free of vulnerabilities (If npm is used).
  • All npm dependencies are still maintained (If npm is used).
  • All other 3rd party code is free of vulnerabilities.
  • The content type prefixes locations with a library path or a content path (to prevent manipulation of URLs leading to other sites).



The code review is not really intended to find bugs, even though that may happen. It’s rather intended to make sure the code is written well on an architectural level, although in many cases this cannot be evaluated objectively. Also, especially when more than one person is working on the code, it is important that it follows guidelines, so it becomes easier to understand and maintain.

H5P Group has issued some best practices and coding guidelines. One may agree with them or think one could do better. But that’s beyond the point. The goal is to make code look similar no matter who wrote it, so it becomes easier to understand any H5P code. Luckily, there is some eslint configuration ready for use that can let your code editor hint at violations of the H5P coding style guide and to fix many of them automatically.

Extra information


H5P content types are written in JavaScript. Most of them do not use any framework such as React or Svelte, because content types are often very small and burdening them with the overhead of a framework may not be worth it. There’s no guideline saying one could not use them, but if one wants to make it easier for inexperienced developers to find their way through H5P code, then one should not introduce new concepts without a real need to.

The same is true for TypeScript. There is nothing wrong with introducing type safety, but again – given the quite different look and handling of code and the rather small size of most H5P content types – using it may not be necessary and confuse less experienced developers. This is not to be confused with an argument against TypeScript. It is only an argument for trying to keep code across different content types similar. It is more likely that the H5P core team will be moving towards JavaScript annotations (as it is becoming a standard) than towards TypeScript (or Flow or Hegel as custom tool solutions).

H5P Group have used and use JSDoc to add type information, and in particular a verbal description of what a function is doing or what a variable is used for. Yes, that information should be transported by good naming anyway.

Stylelint could be used to also lint SCSS (just using the recommended configuration).

A framework that every H5P content type has to use at least once (but does not need to provide) is jQuery. It is still part of the H5P core, but will be removed at some point in time, because JavaScript has evolved and has made the use of jQuery obsolete to a large degree. New code should not make use of it as the code will definitely look different and need some extra treatment.

The H5P core team uses JSDoc comments to hint at the types of variables and return values. Those yield the benefit of having short explanations for variables and functions (on top of hopefully clear naming). One can amend the eslint configuration to use jsdoc to also check for proper use of JSDoc comments.

Many older H5P content types created by the H5P core team still use ES5 syntax that did not allow import modules and had no class notation or arrow functions. Using the latter should be the preferred way, as the handling of ES5 is particularly confusing for developers without much JavaScript experience.

When using a build chain, it is okay to use SCSS/SASS, because the nesting syntax really helps to understand the style sheets hierarchy.


H5P content types usually run in an iframe, and browsers cannot automatically detect programmatic changes in the height of the content. Content types will need to explicitly inform the H5P core that they may have resized, so the H5P core can in turn resize the iframe. The other way around, the iframe may be resized (e.g. when resizing the browser window), and the H5P core will inform the content type about that change, so it can adjust to the new size if required. It may also happen that a parent content type informs its children about a resize independently of the iframe size. H5P provides a dedicated messaging mechanism for that purpose that should be used, not custom browser resize listeners or similar.

One should not fire a resize event in a content type too often, because the process that gets triggered may require quite some resources. A resize should only be triggered when really needed.


Authors may not fill in all fields in the editor. And parent content types with a visual editor will instantiate a child content type without any parameters at all. As a consequence, all relevant parameters should be sanitized when instantiating a content type to avoid a crash. That’s also a security aspect.

Element id attributes

If it is required to give an id attribute to a DOM element, it should be made sure that it is unique. One needs to keep in mind that a parent content type could hold multiple instances of a subcontent type, so it is not enough to e.g. use a static id. One can use  H5P.createUUID from H5P core to obtain a Universally Unique Identifier (UUID).

Element classes

Even though it may not be strictly required, all DOM elements should have a dedicated CSS class name in order to make customizing the visual appearance easier.

Exposed functions and variables

It’s recommended to make all major functions and variables public, so people can easily read and modify them from the outside, e.g. determine the state of some variable or override a function to change the behavior. Security should not be neglected, but we’re not writing code for a nuclear power plant. If you prefer to have variables and functions secluded, that’s totally up to you however.

Assumptions about the DOM

One should not assume that an H5P content type’s DOM will be attached to the document’s DOM tree when the content type is instantiated or attached. This may happen at a later point in time. One could use a one time intersectionObserver for those cases where one needs the content type’s DOM to be attached to the document’s DOM before performing some action.


The library.json file describes some fundamentals of the H5P library. One of the properties to set is embedTypes. It can take the values div and iframe and determines whether the content type will render in a mere div element or inside an iframe. While iframes come with some challenges of their own, they shield the H5P content types from the parent page’s style sheets which too often bleed into the content if using div. That’s why iframe should be preferred.


The group element in semantics.json behaves unintuitively. It does return a plain type element if the group’s field array contains only one element, but an array if it contains multiple elements. If one requires to use a group with one element only, one should add a “dummy” element of type boolean and give it the ”widget”: “none” property/value. That way, the editor will behave as if there was only one element while in the parameters there will be an array with the values for the editor element and the invisible dummy element. That way, if later on the group needs to take more than one element, on does not need to upgrade existing contents’ parameter data structure in upgrades.js. Otherwise it would require to bump the minor version number potentially resulting in multiple version upgrades in other content types – a task that’s not that much fun for H5P releases 🙂


  • Resizing is fine.
    • When the content changes in height, the H5P iframe also gets resized and no content is cut off at the bottom.
    • When there are child content types, they get informed about a resize that may be required.
    • Circular dependencies – a child content type triggering a resize which causes a parent type to resize which in turn asks child content types to resize – are prevented.
    • Custom resize events are triggered with a proper frequency.
    • No custom resize mechanisms are used.
  • The DOM is taken care of well.
    • All id attributes of DOM elements are unique and have a random component at least.
    • All DOM elements have a CSS class.
    • The content type is aware that its DOM may not be attached to the page DOM yet.
  • All relevant parameters are sanitized when instantiating an H5P content type.
  • No libraries that require a certain browser are used.
  • No server-side components that are not controlled by the H5P core are used.
  • The code completely follows the H5P coding style guidelines.
  • TypeScript is used thoroughly, so no use of “any”, etc. (if used at all).
  • All functions have proper JSDoc comments (when using JavaScript, not TypeScript).
  • The jQuery framework is not used except for answering H5P core’s call to the attach function or using other H5P core functions that require it.
  • All major functions are exposed.
  • The code uses class notation and arrow functions, not ES5 style definitions.
  • There are no magic numbers.
  • Functions and variables are properly named to be self-explanatory.
  • The H5PIntegration object is not queried directly (unless there’s no other way to get the desired information yet).
  • Comments are used to primarily explain why something is done, not what is done (that should be clear from the code).
  • The library.json file follows the H5P specification for library.json.
    • All required dependencies for the view and the editor are specified.
    • The embedTypes property uses [“iframe”] as the value.
  • The semantics.json file follows the H5P specification for semantics.json.
    • There are no conflicting versions of a content type as a dependency in the whole dependency tree.
    • A group field has at least two entries in its fields property.
  • library.json/semantics.json: The latest versions of subcontent libraries are used unless an earlier version is really required.
  • There are no unused H5P libraries as dependencies in library.json.
  • There are no TODO comments pointing towards crucial things that should be done now or issues that do not require discussion or some other major effort.


There are certain things that a repository for an H5P content type needs to follow or at least should follow to fit into the H5P development ecosystem. Some are derived from the H5P specification directly, some are based on the initially mentioned premise to keep things simple for rather inexperienced developers.

Extra information


When packing H5P libraries with the H5P CLI tool, one should make sure that only relevant files are put into the library. For instance, a package.json file could end up in the library and be installed on some host system, because json files are allowed inside H5P libraries. That would not break anything, but simply waste the platform’s resources.

The .h5pignore file works the same way as a .gitignore file. While git will use the latter to identify files and directories that should not be pushed to a remote server, the H5P cli tool will use the former to identify files inside the repository that are relevant for building distribution files but should not be put into the H5P library, e.g. source code and configuration files.


When using npm, the package.json file contains a bunch of configuration items. In order to make it as easy as possible for inexperienced developers to work with H5P repositories, the scripts should be named the same everywhere:

  • build to build the distribution files for production
  • watch to continuously build distribution files for development
  • lint to lint files without fixing anything

Tools in the build chain

There are many tools that one can use to create a build chain. And there will be many that are better than the ones that are suggested now. The rationale here it to try to make repositories accessible for inexperienced developers, so they should use the tools that one will come across normally:

  • eslint for linting and formatting (not prettier, for instance, which would not work for the H5P style guide anyway) – note that this may change when eslint removes formatting from its set of features
  • webpack for bundling (instead of vite, for instance)

It is common to use Babel as part of a build chain in order to transpile code to older versions of JavaScript that older browsers may not understand otherwise. If one does not use all the latest JavaScript shenanigans, one could potentially live without Babel, but it’s fine to have it.

H5P dependencies

The core of H5P can only handle one version of an H5P library at a time. If using other H5P libraries as dependencies, either via library.json or via semantics.json, one needs to ensure that along the whole dependency tree there are no dependencies to two different versions of a library. Otherwise the content may crash.

For instance, one could assume that H5P.Column had dependencies to H5P.CoursePresentation 1.0 and H5P.TrueFalse 1.3. That version of Course Presentation could have a dependency to H5P.TrueFalse 1.2. There would be two different versions required for a True False Question. If an author put a True False Question into a Column and also a Course Presentation that included a True False Question, that could cause trouble. The dependency in Column would need to be H5P.TrueFalse 1.2 as well.

The H5P CLI tool has a command to check for those kinds of inconsistencies: h5p find-inconsistencies $LIBRARY_DIR. All the dependencies would need to be in the same directory for being able to check them however, so it’s easiest to perform this inside the libraries directory of an H5P integration if one is not using the H5P CLI tool for development anyway.


  • There is an icon.svg file representing the content type for the H5P Hub.
  • The package.json file is fine (if used).
    • There is a build script in package.json that builds the distribution files for production.
    • There is a watch script in package.json that continuously builds the distribution files for development.
    • There is a lint script in package.json for linting without fixing (if a linter is configured).
  • All files that should not end up in the distribution files are listed in .h5pignore.
  • Distribution files meant for production (using the build script) are minified and do not include sourcemaps.
  • A proper license is set in a LICENSE file and in library.json.
  • eslint is used for linting (if relevant).
  • webpack is used for bundling (if relevant).
  • Branch names
    • The main branch of the repository is called master.
    • There is a branch named release containing the currently released version’s code.

This post was created in cooperation with the NDLA and is licensed under a CC0 license.