About XBlock Asides#

Tags: developer concept

An XBlock Aside is a class that injects content into the rendered views of existing XBlocks without modifying those XBlocks. Asides let you add behavior, data, and UI elements to many XBlock instances at once, across XBlock types you do not own, while preserving the host XBlock’s code, fields, and Open Learning XML (OLX) representation.

Warning

Asides have many limitations, including issues around course import and export. See Limitations for more detail.

What an Aside Is#

An Aside is a Python class that subclasses XBlockAside, declares one or more view-injection methods using the aside_for() decorator, and is registered with the platform through a Python entry point in the xblock_asides.v1 group. When the platform renders an XBlock view, the runtime collects every applicable Aside, invokes its matching Aside view, and appends the resulting fragments to the host XBlock’s rendered fragment.

An Aside is not a child XBlock. It does not appear in the course outline, it does not have its own URL, and it cannot be added to a course like a regular block. It exists only in relation to a host block, and its lifecycle is bound to that host block’s lifecycle.

For the precise API surface, see XBlock Asides Reference.

The Problem Asides Solve#

When you want to enhance the behavior of an XBlock that you did not write, you have three options:

  1. Fork the XBlock and modify it directly.

  2. Replace the XBlock with a new XBlock that wraps the original.

  3. Attach an Aside to the existing XBlock.

The first two options carry significant costs. Forking creates a parallel codebase that must be maintained against upstream changes. Replacing the XBlock requires every existing course that uses the original to migrate, and it does not scale when you want to enhance many different XBlock types in the same way.

Asides solve this by externalizing the enhancement. The host XBlock is not modified. The same Aside can apply to a Video block, a Problem block, or any other block type, by overriding a single classmethod. Asides can serialize their own scoped fields during course import and export.

Reach for an Aside when all of the following are true:

  • You want to enhance one or more existing XBlock types without forking them.

  • The enhancement is conceptually layered on top of the block, not a replacement for any of its behavior.

  • The enhancement should apply to many block instances, possibly across block types, without per-instance configuration in the course outline.

  • The enhancement may need its own settings or stored data, scoped to the block instance.

Reach for something else when:

  • You are creating a brand new piece of course content. Write an XBlock.

  • You only need to react to platform events. Consider an Open edX event receiver.

How an Aside Relates to Its Host Block#

The runtime maintains a many-to-many relationship between asides and host blocks at runtime, but each Aside instance is bound to exactly one host block during a single render. The relationship is established in three stages.

Per-Block Filtering#

For each candidate Aside type, the runtime instantiates the Aside and asks it whether it should apply to this specific block by calling its should_apply_to_block() classmethod. The default implementation returns True. Real-world asides almost always override this method to restrict themselves to specific block types, course contexts, or feature flags.

Rendering and Layout#

For each Aside that survives filtering, the runtime invokes the Aside method that was decorated with @XBlockAside.aside_for(view_name) for the view being rendered. The Aside method returns a Fragment, the runtime wraps that fragment with identifying markup, and the runtime appends the wrapped fragment to the host block’s rendered output. A runtime can override layout_asides() to control where and how the Aside fragments are placed.

Why Asides Are Worth the Trouble#

The framing above describes the trade-offs from the perspective of someone choosing among extension mechanisms. The deeper reasons asides exist, and remain useful, come from the production deployments that depend on them.

Multiple Block Types, One Implementation#

A single Aside class can decorate Video blocks, Problem blocks, and any other block type the author chooses, by checking block.category or block.scope_ids.block_type inside should_apply_to_block. The MIT Open Learning chat Aside, for example, attaches an “AskTIM” chat button to both Video and Problem blocks from a single class, with one entry point. Without asides, the same outcome would require either two parallel forks or replacement blocks for both types.

Course Author Control#

An Aside can declare its own scoped fields, just like an XBlock. By exposing those fields in an author view, an Aside gives course authors a UI to enable or disable the enhancement on a per-block basis. The settings are stored under the Aside’s own scope, not the host block’s, so they are preserved across exports and imports without any change to the host block’s data model.

OLX Export and Import#

When a course is exported to OLX, the platform serializes each Aside as an XML child element under its host block, named after the Aside’s entry point name. On import, the runtime reconstitutes the asides automatically. This means an Aside-enhanced course is portable, with limitations described below.

Reaching Outside the Iframe#

An Aside’s fragment renders inside the same iframe as its host block, so its JavaScript is confined to that iframe unless it does something about it. The browser’s postMessage API is the way out. The Learning micro-frontend already recognizes a handful of built-in message types (for things like opening a modal or resizing the frame) that any Aside can send for free, with no setup. Beyond those, an Aside can post a message with any custom type it defines, but nothing reacts to a made-up type by default — something has to be listening for it. That listener is a deployment-time addition, registered through the Learning MFE’s own runtime configuration, not a fork of the MFE itself. MIT Open Learning’s “AskTIM” chat button (the ol-openedx-chat Aside, described next under “Real-World Examples”) works exactly this way: its JavaScript posts a message, and a small companion script MIT deploys alongside the MFE listens for it and opens a chat drawer. See XBlock Asides Reference for the full mechanics and Add an XBlock Aside for a worked example of both halves.

Real-World Examples#

Three implementations in the wild illustrate the range of what asides can do, from a bare waffle-flag toggle to a fully wired author-facing UI.

Rapid Response XBlock#

The rapid-response-xblock from MIT Open Learning is a single Aside that applies to Problem blocks restricted to a single multiple-choice response type. It overlays an instructor-only control on the problem in the LMS that lets a live instructor open and close response windows during a lecture, and it renders a real-time chart of student responses. A Boolean field enables or disables it per problem, exposed through a checkbox in its studio_view; a companion, Django-settings-gated author_view shows the same checkbox in Studio’s author preview. The repository name calls it an “xblock” but the implementation is purely an Aside.

Open Learning Chat Aside#

The ol-openedx-chat Aside, also from MIT Open Learning, attaches an “AskTIM” chat button to Video and Problem blocks. The button opens a context-aware chat drawer that streams messages to a backend large language model, passing block-specific context such as a video transcript identifier or a problem’s siblings. A single Aside class, registered as one entry point, handles both block types and uses should_apply_to_block to gate on a course-level waffle flag, the block-type check, and a course-level enabled setting together. A course-author checkbox in its author_view toggles the button per block, backed by a scoped field and an AJAX handler. See Add an XBlock Aside for a full walkthrough of this pattern.

Structured Tags Aside#

The StructuredTagsAside ships with the Open edX platform code by default, in cms/lib/xblock/tagging/tagging.py. It attaches a tag picker to Problem blocks so course authors can apply structured tags for content search and organization. Unlike the other examples, its entire author-facing configuration UI is its AUTHOR_VIEW fragment — there is no separate toggle checkbox, just the tag picker itself, and the picker only appears at all when Studio’s aside-rendering gate (see XBlock Asides Reference) is enabled. It has no should_apply_to_block override; it instead checks isinstance(block, ProblemBlock) inline inside its view method. Its own author view method is, confusingly, still named student_view_aside even though it is decorated for AUTHOR_VIEW — a reminder that an Aside’s view method names are conventions, not requirements enforced by the framework.

Limitations#

Asides are a real, working feature in production deployments, but the ecosystem around them is incomplete. The list below is drawn from the state of the codebase as of the Sumac release and from a 2025 Open edX Conference talk by Peter Pinch of MIT Open Learning. Read it before committing to an Aside-based design.

No Native Authoring Story in the Course Authoring MFE#

The Authoring micro-frontend has no native code for rendering or toggling Asides — it does not know Asides exist. Its unit editor embeds the Studio unit page in an iframe, and whatever that page renders — including an Aside’s author_view UI (a checkbox, a tag picker, whatever the Aside renders), gated by StudioConfig — shows up inside it. There is no separate, MFE-specific toggle to configure. The embedded page and the MFE aren’t otherwise isolated from each other, either — they already exchange at least one real postMessage today. See XBlock Asides Reference for the full mechanics of both the Studio-side gate and this iframe relationship.

A Host Block Vanishes Silently If Its Aside Is Uninstalled#

If a course was authored with an Aside attached to one of its blocks, and that Aside is later uninstalled or its entry point renamed, re-importing the course does not raise any error — the import reports success. What actually happens is worse than losing the Aside’s data: the host block that carried the Aside is dropped from the course entirely. It does not appear as an error block or a placeholder; it simply isn’t there. There is no warning that anything was lost. If you depend on a course’s blocks surviving its lifecycle, treat uninstalling or renaming an Aside that’s in use as a breaking, silent change to every course that has it attached to a block, and audit affected courses before doing so.

Not All XBlocks Round-Trip Through OLX#

OLX export and import for asides depends on the host XBlock cooperating with the export process. Some XBlocks, including ORA2, do not preserve Aside data through their export and import paths. If your Aside must survive a course export and re-import on a course that uses one of these blocks, test the round trip end to end before depending on it. (This is a different failure mode from the previous limitation: here the host XBlock is present but doesn’t cooperate with serialization; there, the Aside itself is simply gone.)

Multiple Asides on a Single Block Are Not Reliable#

The runtime supports multiple Aside types decorating the same block in principle, but interactions between asides on the same block are not well-tested, and this goes deeper than rendering. Two Asides attached to the same block type that happen to declare an identically-named Scope.content or Scope.settings field silently share the same stored value on the platform’s Split modulestore — confirmed by directly reproducing the collision. See XBlock Asides Reference for the mechanism and the reproduction. Two Asides that both decorate student_view can also render correctly in isolation and break when combined, independent of field naming. If you need multiple Asides on the same block type, give every field a name that’s unlikely to collide with another installed Aside, and build a single Aside that composes the behaviors instead of relying on two independent Asides to coexist wherever you can.

JavaScript Library Loading Is Limited#

Asides use the same fragment-based JavaScript loading mechanism as XBlocks, which assumes a single set of static assets. If your Aside needs a JS library that is not already loaded by the host page, you must add it through the fragment, and you must handle ordering and conflicts yourself. There is no shared Aside-level mechanism for declaring library dependencies.

Where to Go Next#

If you are ready to build an Aside, start with Quickstart: Build Your First XBlock Aside. If you already have a target XBlock in mind and want a step-by-step recipe, read Add an XBlock Aside. For the complete list of classes, decorators, methods, and entry points, consult XBlock Asides Reference. The rapid-response-xblock, ol-openedx-chat, and StructuredTagsAside implementations described above are also worth reading directly as reference material.

See also

XBlock Asides Reference (reference)

The complete API surface for XBlockAside and its runtime hooks.

Add an XBlock Aside (how-to)

A step-by-step recipe for adding an Aside to existing XBlocks.

Quickstart: Build Your First XBlock Aside (quickstart)

A beginner-friendly walkthrough from zero to a running Aside.

Hooks Extension Framework (concept)

An alternative extension mechanism for non-view-based behaviors.

Maintenance chart

Review Date

Working Group Reviewer

Release

Test situation