Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
245 changes: 245 additions & 0 deletions source/developers/concepts/about_xblock_asides.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
.. _About XBlock Asides:

###################
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this mean, "across XBlock types you do not own" ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude describes what it meant:

By "XBlock types you do not own," I meant XBlocks whose source code you don't maintain — typically core platform blocks like problem and video (owned by the edx-platform team), or third-party blocks like ORA2, or blocks maintained by another organization. If you wanted to enhance one of those, your non-aside options are: fork it (and maintain a parallel copy), upstream a PR (slow, narrow design space, may be rejected), or wrap it in a replacement block (forces every existing course to migrate). Asides give you a fourth option: attach behavior from your own installable package, leaving the upstream code untouched.

That said — if you're asking, the phrasing wasn't self-explanatory, which is its own signal. Some options to make it clearer:

Option A (minimal — define the term in place):

across XBlock types whose source you do not maintain (for example, the platform's built-in problem and video blocks, or third-party blocks installed as separate packages)

Option B (recast — drop the ownership framing, lead with the practical consequence):

Asides let you add behavior, data, and UI elements to many XBlock instances at once — including instances of XBlock types maintained by other teams or installed as third-party packages — while preserving the host XBlock's code, fields, and Open Learning XML (OLX) representation.

Option C (cut it):

Asides let you add behavior, data, and UI elements to many XBlock instances at once, while preserving the host XBlock's code, fields, and OLX representation.

The "without modifying those XBlocks" earlier in the same paragraph already implies this, so cutting the phrase entirely is defensible. The rest of the concept doc, particularly the "The Problem Asides Solve" section, makes the fork-vs-replace-vs-aside trade-off explicit.


Do any of these options feel correct, and worth applying?

XML (OLX) representation.

.. contents:: Contents
:local:
:depth: 1

What an Aside Is
****************

An Aside is a Python class that subclasses :class:`~xblock.core.XBlockAside`,
declares one or more view-injection methods using the
:func:`~xblock.core.XBlockAside.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 :ref:`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:

#. Fork the XBlock and modify it directly.
#. Replace the XBlock with a new XBlock that wraps the original.
#. 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
:meth:`~xblock.core.XBlockAside.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
:meth:`~xblock.runtime.Runtime.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.

Real-World Examples
*******************

Two implementations in the wild illustrate the range of what asides can
do.

Rapid Response XBlock
=====================

The `rapid-response-xblock`_ from MIT Open Learning is a single Aside that
applies to Problem blocks. 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. Course authors enable it per problem in Studio. 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 and per-course settings.

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 Authoring Story in the Course Authoring MFE
==============================================

The Studio author view for an Aside is rendered by the legacy course
authoring frontend. The current Authoring micro-frontend has no
defined location to display Aside author UI. If your project depends on the
new MFE for authoring, plan to render the Aside's author UI through a
different mechanism, or accept that authors will use the legacy Studio for
this part of the workflow.
Comment on lines +174 to +182
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are 2 views, AUTHOR_VIEW and STUDIO_VIEW.
Previously, we used STUDIO_VIEW to display any aside field, like enable/disable checkbox, etc., and it was displayed in the editor. By editor, I mean the edit view that opens when we click the edit button of an XBlock in Studio. There used to be a settings tab in that editor. This is not available in the authoring MFE.
Alternatively, we shifted to AUTHOR_VIEW and used it to display the fields on the unit page in authoring MFE, and this works fine.
In ol_openedx_chat aside, we have an author_view, and it displays the checkbox field in the unit view like:

Image

We should note that we are using an XBlock handler to save the state of this checkbox. XBlock handler is called from JS when state of this checkbox changes.


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.
Comment on lines +188 to +191
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kdmccormick, @pdpinch : Do you know if this because of the "pure XBlock" vs. "openedx-platform baked in XModule legacy" serialization paths? Or is this something different?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arslanashraf7 or @asadali145 do you recall the details of this?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ormsbee If I understand it correctly, this is related to openedx/openedx-platform#36576.


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. Two asides that both decorate ``student_view`` on the same
block may render correctly in isolation and break when combined. If you
need this, build a single Aside that composes both behaviors rather than
relying on two independent asides to coexist.

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
:ref:`XBlock Aside Quickstart`. If you already have a target XBlock in mind
and want a step-by-step recipe, read :ref:`Add an XBlock Aside`. For the
complete list of classes, decorators, methods, and entry points, consult
:ref:`XBlock Asides Reference`.

.. _rapid-response-xblock: https://github.com/mitodl/open-edx-plugins/tree/main/src/rapid_response_xblock
.. _ol-openedx-chat: https://github.com/mitodl/open-edx-plugins/tree/main/src/ol_openedx_chat
.. _xblock-sdk: https://github.com/openedx/xblock-sdk
Comment on lines +221 to +223
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, that StructuredTagsAside was helpful for us.


.. seealso::

:ref:`XBlock Asides Reference` (reference)
The complete API surface for ``XBlockAside`` and its runtime hooks.

:ref:`Add an XBlock Aside` (how-to)
A step-by-step recipe for adding an Aside to existing XBlocks.

:ref:`XBlock Aside Quickstart` (quickstart)
A beginner-friendly walkthrough from zero to a running Aside.

:ref:`Hooks Extension Framework` (concept)
An alternative extension mechanism for non-view-based behaviors.

**Maintenance chart**

+--------------+-------------------------------+----------------+--------------------------------+
| Review Date | Working Group Reviewer | Release |Test situation |
+--------------+-------------------------------+----------------+--------------------------------+
| | | | |
+--------------+-------------------------------+----------------+--------------------------------+
Loading