Display H5P content without the need for an H5P server.
This library can be installed and used in two main ways, depending on your project setup. After installation, ensure you have an H5P file (.h5p
) and its contents extracted into a folder in your project. For a guide on this, see the Extracting H5P section.
This approach is suitable for simpler projects or when you don't have a JavaScript build pipeline. You have two options: using a CDN or downloading files.
This is the quickest way to get started with manual installation. You can link to the player files directly from a CDN like jsDelivr.
Include these links in your HTML:
<!-- Replace @latest with a specific version, e.g., @3.9.0, for production -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/h5p-standalone@latest/dist/styles/h5p.css">
<script src="https://cdn.jsdelivr.net/npm/h5p-standalone@latest/dist/main.bundle.js" charset="UTF-8"></script>
Note: While @latest
gets the newest version, it's recommended to pin to a specific version in production environments to avoid unexpected updates. For example, use [email protected]
instead of h5p-standalone@latest
.
Example HTML page:
<html>
</head>
<body>
<div id="h5p-container"></div>
<script src="https://cdn.jsdelivr.net/npm/h5p-standalone@latest/dist/main.bundle.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const options = {
h5pJsonPath: '/path/to/your/h5p-folder', // Path to your self-hosted H5P content directory
frameJs: 'https://cdn.jsdelivr.net/npm/h5p-standalone@latest/dist/frame.bundle.js',
frameCss: 'https://cdn.jsdelivr.net/npm/h5p-standalone@latest/dist/styles/h5p.css',
};
const el = document.getElementById('h5p-container');
// H5PStandalone is globally available when main.bundle.js is included via a script tag
new H5PStandalone.H5P(el, options);
});
</script>
</body>
</html>
Your H5P content (specified in h5pJsonPath
) still needs to be hosted on your own server or accessible path.
This method involves downloading the pre-built files from the releases page and hosting them yourself.
Installation Steps:
- Download the latest release source code (e.g.,
h5p-standalone-vX.X.X.zip
) from the releases page. - Extract the downloaded zip file.
- Locate the
dist
folder within the extracted contents. Copy thisdist
folder into a suitable location in your project (e.g., rename it toh5p-player
and place it in anassets
directory likeassets/h5p-player
).
Usage Example (Self-Hosted):
After placing the files from the dist
folder into your project:
-
Add an HTML
div
element where you want to display the H5P content. Thisdiv
must have a uniqueid
.<div id="h5p-container"></div>
-
Include the H5P standalone main script (
main.bundle.js
) in your HTML page. Modify thesrc
path according to where you placed thedist
folder's contents.<script type="text/javascript" src="assets/h5p-player/main.bundle.js" charset="UTF-8"></script>
-
Call the H5P player in a subsequent script tag, providing the
id
of yourdiv
and paths to the H5P content and the necessary player assets (frame.bundle.js
andh5p.css
from your self-hosteddist
folder contents).document.addEventListener('DOMContentLoaded', function () { const el = document.getElementById('h5p-container'); const options = { h5pJsonPath: '/path/to/your/h5p-folder', // Path to the extracted H5P content frameJs: 'assets/h5p-player/frame.bundle.js', // Path to player's frame.bundle.js frameCss: 'assets/h5p-player/styles/h5p.css', // Path to player's h5p.css }; new H5PStandalone.H5P(el, options); });
This method is suitable for projects that utilize a JavaScript module bundler and a build process.
Install the player using your favorite package manager, for example:
# pnpm
pnpm install h5p-standalone
# yarn
yarn add h5p-standalone
# npm
npm install h5p-standalone
After installation via a package manager, you can import the player into your JavaScript modules.
Add an HTML element to your page where the H5P content will be rendered:
<div id="h5p-container"></div>
Then, initialize the player in your JavaScript:
import { H5P } from 'h5p-standalone'; // ES6 Import
// const { H5P } = require('h5p-standalone'); // For CommonJS environments
const el = document.getElementById('h5p-container');
const options = {
h5pJsonPath: '/path/to/your/h5p-folder', // Path to the extracted H5P content
frameJs: '/assets/h5p-standalone/frame.bundle.js', // Adjust path as per your project structure
frameCss: '/assets/h5p-standalone/styles/h5p.css', // Adjust path as per your project structure
};
new H5P(el, options);
Note on frameJs
and frameCss
paths: When using with a bundler, you'll need to ensure that the frame.bundle.js
and h5p.css
files (from the dist
folder of this package) are copied to a location accessible by your application (e.g., your public assets
directory) during your build process. The paths in the options
object must then point to these served files.
A detailed description of all H5P player arguments, including advanced options for customization, xAPI, and state restoration, is provided in the Advanced Usage section.
H5P files (with the .h5p
extension) are essentially zip archives. Before you can use them with this player, you need to extract their contents into a folder within your project.
Important Note on H5P Libraries:
Content exported directly from h5p.org might not include all the necessary H5P libraries required for the content to function correctly. This is because h5p.org optimizes exports by assuming the libraries are already present on the target platform.
If you encounter issues with missing libraries, you have a couple of options:
- Manually download libraries: You might need to identify the missing libraries and download them separately from h5p.org, then place them in the appropriate H5P content folder.
- Use bundled H5P content: Obtain your H5P content from a source that explicitly bundles all required libraries within the
.h5p
file.
The standalone H5P player constructor new H5PStandalone.H5P(el, options)
or new H5P(el, options)
accepts two arguments:
- An HTML element where the H5P iframe will be embedded.
- A JSON object with the following options:
- Basic options
Option name | Required | Description |
---|---|---|
h5pJsonPath |
Yes | String. Path to the H5P content folder. |
frameCss |
Yes | String. URL to the standalone player h5p.css . |
frameJs |
Yes | String. URL to the standalone player frame.bundle.js . |
id |
No | String. Player unique identifier. Randomly generated by default. |
librariesPath |
No | String. Path where the player should find the H5P content libraries. Defaults to the h5pJsonPath . |
contentJsonPath |
No | String. Path where the player should find the H5P content.json file. Defaults to {h5pJsonPath}/content . |
frame |
No | Boolean. Whether to show the H5P player frame and buttons. Default is false . |
copyright |
No | Boolean. Whether to display the copyright button. Default is false . |
export |
No | Boolean. Whether to display a download button. Default is false . |
icon |
No | Boolean. Whether to display the H5P icon. Default is true . |
downloadUrl |
No | String. A path or URL that provides the .h5p file for download. Used by the export button. |
fullScreen |
No | Boolean. Whether to enable the fullscreen button (if supported by the browser). Default is false . |
embed |
No | Boolean. Whether to display the embed button. Default is false . (N.B. Setting this to true requires embedCode to be provided). |
embedCode |
Yes, if embed is true |
String. Embed/iframe code that users can copy. Placeholders :w and :h will be replaced. See Caveats for Embed Code. |
customCss |
No | String or Array of strings. Path(s) to custom CSS file(s). |
customJs |
No | String or Array of strings. Path(s) to custom JavaScript file(s). Useful for adding external libraries. See the Frequently Asked Questions (FAQ) for detailed examples like MathJax integration. |
reportingIsEnabled |
No | Boolean. Set to true to enable the submit button, particularly for content like Interactive Book. Defaults to false . |
xAPIObjectIRI |
No | String. An identifier for a single unique Activity, utilized when generating the xAPI object field. Defaults to page host + pathname. |
- User state & data (refer to the Previous State Restoration section for more details)
Option name | Required | Description |
---|---|---|
contentUserData |
No | Array. Used to pass previously saved user state to the player. Expects an array of objects, e.g., [{ dataType: 'state', previousState: 'JSON_STRING_OF_STATE', subContentId: '*' }] . |
saveFreq |
Yes, if contentUserData or ajax.* is set |
Number. How often (in seconds) the current user engagement content state should be autosaved. Set to false to disable. |
postUserStatistics |
No | Boolean. Indicates if H5P should post results upon a finish event. Default is false . (Requires ajax.setFinishedUrl to be set). |
ajax |
No | Object. Required if you need H5P to manage a learner's state via AJAX calls. |
ajax.setFinishedUrl |
No | String. URL where H5P should POST the results upon a finish event. (Requires postUserStatistics to be true ). |
ajax.contentUserDataUrl |
No | String. Endpoint for H5P to GET and POST the current user state. (Requires user property to be set). |
user |
No | Object. Current user data object. |
user.name |
Yes, if user is provided |
String. Name of the xAPI actor. |
user.mail |
Yes, if user is provided |
String. Email of the user, uniquely identifying the xAPI actor. |
Note:
- One can use absolute URL for
frameCss
,frameJs
, and for other path options(h5pJsonPath
,librariesPath
, &librariesPath
) - Any path that starts with a forward slash
/
is treated as relative to the site root. - Any path starting with a dot is treated to be in respect to the current page directory.
import { H5P } from 'h5p-standalone';
const el = document.getElementById('h5p-container');
const options = {
id: 'exercise-one',
frameJs: './assets/frame.bundle.js', // Relative path to player script
frameCss: './assets/styles/h5p.css', // Relative path to player styles
h5pJsonPath: '/path/to/h5p-folder',
contentJsonPath: '/path/to/h5p-folder', // content.json is in the same folder as h5p.json
librariesPath: '/path/to/shared/libraries', // Optional: shared libraries path
frame: true, //required to display copyright, embed, & export buttons
copyright: true,
export: false,
icon: true,
downloadUrl: '/path/to/exercise-one.h5p',
fullScreen: true, //enable fullscreen button
embed: true,
embedCode: '<iframe width=":w" height=":h" src="https://YOURSITE/h5p-embed/exercise-one" frameBorder="0" scrolling="no" style="width:100%"></iframe>',
customCss: [
'/path/to/custom-styles.css',
'/path/to/another-theme.css'
],
customJs: '/path/to/your-custom-script.js', // Can be a single string or an array of strings
reportingIsEnabled: true, // Enable submit button for content like Interactive Book
};
new H5P(el, options)
.then(() => {
// do stuff
});
To render multiple H5Ps, your code must be async aware.
import { H5P } from 'h5p-standalone';
const player1Options = {
h5pJsonPath: '/h5p/exercise-one',
frameJs: '/assets/frame.bundle.js',
frameCss: '/assets/styles/h5p.css',
};
const player2Options = {
h5pJsonPath: '/h5p/exercise-two',
frameJs: '/assets/frame.bundle.js',
frameCss: '/assets/styles/h5p.css',
};
const player1 = new H5P(document.getElementById('h5p-container-1'), player1Options);
//example using top level await
await new H5P(document.getElementById('h5p-container-1'), player1Options);
await new H5P(document.getElementById('h5p-container-2'), player2Options);
To listen for xAPI events emitted by the player, you must wait for the player to finish loading and initializing the required content libraries. You can find more info about xAPI events here https://h5p.org/documentation/x-api
const el = document.getElementById("h5p-container");
const options = {
h5pJsonPath: "/h5p-folder",
frameJs: "/assets/frame.bundle.js",
frameCss: "/assets/styles/h5p.css",
};
new H5PStandalone.H5P(el, options).then(function () {
H5P.externalDispatcher.on("xAPI", (event) => {
//do something useful with the event
console.log("xAPI event: ", event);
});
});
This section describes how to save and load a user's progress and interactions within an H5P content. This "user state" is distinct from xAPI events, which are typically used for reporting completion or scores to a Learning Record Store (LRS). Restoring user state allows learners to resume their work exactly where they left off.
There are two main approaches to manage user state with this player:
In this method, your application is responsible for saving and loading the H5P state in your preferred storage solution anywhere. You provide the previously saved state to the player via the contentUserData
option and implement functions to retrieve and save the state as needed.
Note: The example below uses top-level await
. You'll need to use this script in an environment that supports it, such as a JavaScript module (<script type="module">
) or wrap the main logic in an async
function. Also, ensure el
(the DOM element for H5P, typically <div id="h5p-container"></div>
) is defined in the scope where this script runs.
function retrieveSavedContentState(contentId){
// π your implementation that returns the state for this content (and of course for this user)
// return the content or null
}
function saveContentState(contentId){
const currentState = window.H5PIntegration.contents[`cid-${contentId}`].contentUserData;
//π you can call your logic to save this state somewhere
}
const CONTENT_ID = 'some-content-unique-id-xxxxx';
const previousStateJSON = await retrieveSavedContentState(CONTENT_ID)
const options = {
id: CONTENT_ID, //should be same as
h5pJsonPath: '/content/course-presentation-one',
frameJs: '/dist/frame.bundle.js',
frameCss: '/dist/styles/h5p.css',
saveFreq: 100, //this is required.
contentUserData: [
{
dataType: 'state',
previousState: previousStateJSON, //should be a json string.
}
]
}
await new H5PStandalone(el, options); //init the h5p player.
//call above function periodically, here we are using same time as this content H5P player saving frequency (multiplied by 1000 to convert into miliseconds)
setInterval(()=>saveContentState(CONTENT_ID), options.saveFreq * 1000);
The saveFreq
option in this manual setup is used to control the setInterval
frequency; if you also have ajax.contentUserDataUrl
configured (for automated state management), H5P core might initiate its own save attempts at this frequency too.
This method relies on the H5P player automatically saving and loading the user state from a backend endpoint that you provide and implement. For this to work, several options must be correctly configured as shown in the example below, including saveFreq
, ajax.contentUserDataUrl
, and the user
object with name
and mail
.
Example Configuration:
const options = {
h5pJsonPath: '/content/course-presentation-one', // Path to your H5P content
frameJs: '/dist/frame.bundle.js', // Path to player's frame.bundle.js
frameCss: '/dist/styles/h5p.css', // Path to player's h5p.css
saveFreq: 100, // Time in seconds for autosaving state
ajax: {
// URL for loading (GET) and saving (POST) user state.
// :contentId is a placeholder replaced by H5P with the actual content ID.
// Other placeholders like :dataType and :subContentId can also be used if your backend expects them.
contentUserDataUrl: '/api/v1/h5p/history/users/123/content/:contentId'
},
user: { // User object is required for automated state saving
name: 'John Doe',
mail: '[email protected]' // Email is used by H5P core to uniquely identify the user
},
};
Backend Implementation:
You are responsible for implementing the backend server logic at the specified contentUserDataUrl
.
- GET Request: When H5P content loads, the player will make a GET request to this URL (with placeholders like
:contentId
replaced) to fetch the user's saved state. Your server should return the state as a JSON response, typically an array containing an object (e.g.,[{ "dataType": "state", "previousState": "{\"some\":\"json-string\"}" }]
). If no state exists for the given user and content, your endpoint should respond with an empty array ([]
). - POST Request: When H5P needs to save the state (triggered by
saveFreq
), it will make a POST request to thecontentUserDataUrl
. The body of the POST request will contain the user state data (typically a JSON string representing the H5P state). Your server should store this data, associating it with the user and content ID.
The contentUserDataUrl
can be structured flexibly to suit your backend routing and data model. H5P replaces known placeholders (like :contentId
, :dataType
, :subContentId
) in the URL string before making the request.
An .h5p
file is a zip archive. To use its contents with this player:
- Rename the H5P file extension from
.h5p
to.zip
. - Extract the renamed file's contents into a folder in your project (e.g.,
my-h5p-content
). This folder is what you'll point to with theh5pJsonPath
option.
Here are some common issues and how to resolve them:
Q: Why are some H5P elements missing or not working when I use content exported from h5p.org?
Exports from h5p.org are optimized and might not bundle all the necessary H5P libraries, as they assume the libraries might already exist on the target platform. To resolve this:
- Get libraries from the content type's
.h5p
: Download the.h5p
file for the specific content type directly from h5p.org (e.g., from the content type's example page). These files usually contain all required libraries. Extract this.h5p
file and copy the necessary library folders (e.g.,H5P.ExampleLibrary-1.0
) into your H5P content'slibraries
folder (the one you are using forh5pJsonPath
). - Use fully bundled content: Obtain your H5P content from a source that explicitly bundles all required libraries within the
.h5p
file. Refer to the Working with H5P Content section for more background.
Q: How do I get the 'Submit' button to show up in Interactive Books (or other content types)?
The 'Submit' button visibility on interactive book is controlled by setting reportingIsEnabled: true
in the player options. See the reportingIsEnabled
description in the Advanced Usage section for more details.
Q: How can I display mathematical formulas or equations?
To display mathematical formulas as described on https://h5p.org/mathematical-expressions, you'll likely need an external library like MathJax. You can load such libraries using the customJs
option in the player configuration.
Here's how you can configure customJs
in the player options to load MathJax:
// Within your player options:
customJs: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js', // Option 1: Using a CDN
// customJs: './path/to/your/local/mathjax/tex-mml-chtml.js', // Option 2: Using a local file
// customJs accepts array if you already have multiple custom scripts, i.e.:
// customJs: [
// './scripts/my-first-custom-script.js',
// 'https://example.com/another-script.js',
// 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js' // or your local path
// ],
Refer to the customJs
description in the H5P Options table for more details on the option itself. Remember that the path to local scripts should be relative to your HTML file or an absolute URL.
Q: How resizer for embed code works
- This library includes an H5P resizer by default. However, to ensure the iframe width resizes promptly with the parent page, add a CSS style setting
width: 100%
to the iframe tag itself. - If you want to allow users to resize the iframe's width and height using H5P's resize handles, set them using the placeholders
:w
and:h
in thewidth
andheight
attributes of the iframe.
An example combining these points:
<iframe width=":w" height=":h" src="https://app.wikonnect.org/embed/YOUR-CONTENT-ID" frameBorder="0" scrolling="no" style="width:100%">
</iframe>
After cloning the repository and installing dependencies with yarn install
, you can modify the project.
To build the distributable files:
yarn build
To run available Cypress tests:
yarn test