Skip to main content
The Custom code block lets you author an interaction Compose does not ship. You write HTML, CSS and JavaScript; the exported course runs it in a sandboxed frame.
This is a first-class block, not an escape hatch. It exports the same way every other block does and runs inside any LMS that runs the rest of the course.

What you author

The sandbox

In the exported course, a custom block runs inside an isolated <iframe>. It inherits nothing from the surrounding page — not the course stylesheet, not its scripts, not its variables. That is deliberate. A broken or heavyweight custom block cannot take the course down with it, and a course containing an author’s experiment still renders and still reports completion. The practical consequences:
  • You cannot read or write the learner’s progress from a custom block.
  • You cannot reach the course’s JavaScript or the SCORM API.
  • A custom block does not gate navigation and does not contribute to the score.
  • Anything you need must be inside the block’s own HTML, CSS and JavaScript.

The block cannot reach the network

A custom block has no network access at all. No fetch, no XMLHttpRequest, no remote images, no scripts or stylesheets from a CDN, no web fonts from a URL.
Isolation stops a custom block reaching the course. It does nothing about the outside world — so without this, a block copied from the internet could quietly call home from inside a course you believed was self-contained, and your LMS would carry it to every learner. So the exported frame declares a content policy that permits only what a self-contained block needs: its own inline script and styles, and data: images, fonts and media. If your block needs an asset, import it into the course and reference it. Compose embeds asset:// references directly into the block when it exports, so they work inside the frame without a network request:
Pick images through the media picker rather than typing a path — see Files and media. Embedded assets are capped at 512 KB each, because they end up base64-encoded inside the exported page; anything larger is left alone and reported as an export warning.
A block that genuinely needs live data — a stock ticker, a shared leaderboard — cannot be built this way. That is the trade for a course that runs in an offline LMS and cannot leak anything.

Auto-styling

With Auto styling on — the default — Compose injects the course’s theme values into the frame before your CSS runs:
  • CSS custom properties for the course’s accent, background, surface and text colours, corner radius and typefaces
  • Sensible base styles keyed off them: typography, links in the accent colour, buttons, inputs and code
So a custom block looks like it belongs in the course without you restyling everything. Your CSS is applied after, so anything you set always wins. Turn auto-styling off and the frame gets a minimal baseline instead — a system font and a small margin — which is what you want if you are bringing a complete design of your own.

In the editor

The canvas and the preview panel show a static code panel for a custom block, not the running frame. To see it actually run, use Preview — the preview is the real exported course, so your block executes there exactly as it will for a learner.

Sharing a custom block

Edit ▸ Share block as file… writes any block, including a custom code block, to a small .ceblock file. Blocks saved into your Blocks folder appear in the Blocks tab of the left panel, ready to insert into any course. Because a custom code block carries its own HTML, CSS and JavaScript, a shared .ceblock is effectively a portable interaction — the closest thing Compose has to a plugin. See Templates and reusable pieces.

Things worth knowing

Courses often run inside an LMS’s embedded viewer, which can be an old engine. The course runtime Compose ships is deliberately conservative for that reason; your custom block should be too if your learners are on managed machines.
A SCORM package is often served from an LMS with no outbound internet, or opened from disk. Anything your block fetches from a CDN will simply not load. Inline what you need.
Compose flags an empty custom code block in the status strip. It cannot check whether your code works — use Preview for that.