Skip to content

Commit 8e16795

Browse files
sarinaclaude
andcommitted
Add developer's docs on XBlock Asides
Co-authored-by: Claude <claude@anthropic.com>
1 parent f09f98e commit 8e16795

5 files changed

Lines changed: 1286 additions & 0 deletions

File tree

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
.. _About XBlock Asides:
2+
3+
###################
4+
About XBlock Asides
5+
###################
6+
7+
.. tags:: developer, concept
8+
9+
An XBlock aside is a class that injects content into the rendered views of
10+
existing XBlocks without modifying those XBlocks. Asides let you add behavior,
11+
data, and UI elements to many XBlock instances at once, across XBlock types you
12+
do not own, while preserving the host XBlock's code, fields, and Open Learning
13+
XML (OLX) representation.
14+
15+
.. contents:: Contents
16+
:local:
17+
:depth: 1
18+
19+
What an Aside Is
20+
****************
21+
22+
An aside is a Python class that subclasses :class:`~xblock.core.XBlockAside`,
23+
declares one or more view-injection methods using the
24+
:func:`~xblock.core.XBlockAside.aside_for` decorator, and is registered with
25+
the platform through a Python entry point in the ``xblock_asides.v1`` group.
26+
When the platform renders an XBlock view, the runtime collects every
27+
applicable aside, invokes its matching aside view, and appends the resulting
28+
fragments to the host XBlock's rendered fragment.
29+
30+
An aside is **not** a child XBlock. It does not appear in the course outline,
31+
it does not have its own URL, and it cannot be added to a course like a
32+
regular block. It exists only in relation to a host block, and its lifecycle
33+
is bound to that host block's lifecycle.
34+
35+
For the precise API surface, see :ref:`XBlock Asides Reference`.
36+
37+
The Problem Asides Solve
38+
************************
39+
40+
When you want to enhance the behavior of an XBlock that you did not write,
41+
you have three options:
42+
43+
#. Fork the XBlock and modify it directly.
44+
#. Replace the XBlock with a new XBlock that wraps the original.
45+
#. Attach an aside to the existing XBlock.
46+
47+
The first two options carry significant costs. Forking creates a parallel
48+
codebase that must be maintained against upstream changes. Replacing the
49+
XBlock requires every existing course that uses the original to migrate, and
50+
it does not scale when you want to enhance many different XBlock types in the
51+
same way.
52+
53+
Asides solve this by externalizing the enhancement. The host XBlock is not
54+
modified. The same aside can apply to a Video block, a Problem block, or any
55+
other block type, by overriding a single classmethod. Course authors keep
56+
control over whether the enhancement is active for a given block, because
57+
asides expose their own scoped fields. The enhancement travels with the
58+
course in OLX export and import.
59+
60+
Reach for an aside when all of the following are true:
61+
62+
* You want to enhance one or more existing XBlock types without forking them.
63+
* The enhancement is conceptually layered on top of the block, not a
64+
replacement for any of its behavior.
65+
* The enhancement should apply to many block instances, possibly across
66+
block types, without per-instance configuration in the course outline.
67+
* The enhancement may need its own settings or stored data, scoped to the
68+
block instance.
69+
70+
Reach for something else when:
71+
72+
* You are creating a brand new piece of course content. Write an XBlock.
73+
* You need to change a behavior that is internal to a single block type and
74+
not visible in any view. Consider a runtime service or a filter from the
75+
Hooks Extension Framework.
76+
* You only need to react to platform events. Consider an Open edX event
77+
receiver.
78+
79+
How an Aside Relates to Its Host Block
80+
**************************************
81+
82+
The runtime maintains a many-to-many relationship between asides and host
83+
blocks at runtime, but each aside instance is bound to exactly one host block
84+
during a single render. The relationship is established in three stages.
85+
86+
Discovery
87+
=========
88+
89+
When the runtime renders an XBlock view, it asks the runtime for the set of
90+
applicable aside types. The default runtime returns every aside class
91+
registered through the ``xblock_asides.v1`` entry point. A runtime may
92+
override this to filter the set further, for example based on the current
93+
user or the course.
94+
95+
Per-Block Filtering
96+
===================
97+
98+
For each candidate aside type, the runtime instantiates the aside and asks
99+
it whether it should apply to this specific block by calling its
100+
:meth:`~xblock.core.XBlockAside.should_apply_to_block` classmethod. The
101+
default implementation returns ``True``. Real-world asides almost always
102+
override this method to restrict themselves to specific block types, course
103+
contexts, or feature flags.
104+
105+
Rendering and Layout
106+
====================
107+
108+
For each aside that survives filtering, the runtime invokes the aside method
109+
that was decorated with ``@XBlockAside.aside_for(view_name)`` for the view
110+
being rendered. The aside method returns a ``Fragment``, the runtime wraps
111+
that fragment with identifying markup, and the runtime appends the wrapped
112+
fragment to the host block's rendered output. A runtime can override
113+
:meth:`~xblock.runtime.Runtime.layout_asides` to control where and how the
114+
aside fragments are placed.
115+
116+
Why Asides Are Worth the Trouble
117+
********************************
118+
119+
The framing above describes the trade-offs from the perspective of someone
120+
choosing among extension mechanisms. The deeper reasons asides exist, and
121+
remain useful, come from the production deployments that depend on them.
122+
123+
Multiple Block Types, One Implementation
124+
========================================
125+
126+
A single aside class can decorate Video blocks, Problem blocks, and any
127+
other block type the author chooses, by checking ``block.category`` or
128+
``block.scope_ids.block_type`` inside ``should_apply_to_block``. The MIT
129+
Open Learning chat aside, for example, attaches an "AskTIM" chat button to
130+
both Video and Problem blocks from a single class, with one entry point.
131+
Without asides, the same outcome would require either two parallel forks
132+
or replacement blocks for both types.
133+
134+
Course Author Control
135+
=====================
136+
137+
An aside can declare its own scoped fields, just like an XBlock. By exposing
138+
those fields in an author view, an aside gives course authors a UI to enable
139+
or disable the enhancement on a per-block basis. The settings are stored
140+
under the aside's own scope, not the host block's, so they are preserved
141+
across exports and imports without any change to the host block's data
142+
model.
143+
144+
OLX Export and Import
145+
=====================
146+
147+
When a course is exported to OLX, the platform serializes each aside as an
148+
XML child element under its host block, named after the aside's entry point
149+
name. On import, the runtime reconstitutes the asides automatically. This
150+
means an aside-enhanced course is portable, with limitations described below.
151+
152+
Real-World Examples
153+
*******************
154+
155+
Three implementations in the wild illustrate the range of what asides can
156+
do.
157+
158+
Rapid Response XBlock
159+
=====================
160+
161+
The `rapid-response-xblock`_ from MIT Open Learning is a single aside that
162+
applies to Problem blocks. It overlays an instructor-only control on the
163+
problem in the LMS that lets a live instructor open and close response
164+
windows during a lecture, and it renders a real-time chart of student
165+
responses. Course authors enable it per problem in Studio. The repository
166+
name calls it an "xblock" but the implementation is purely an aside.
167+
168+
Open Learning Chat Aside
169+
========================
170+
171+
The `ol-openedx-chat`_ aside, also from MIT Open Learning, attaches an
172+
"AskTIM" chat button to Video and Problem blocks. The button opens a
173+
context-aware chat drawer that streams messages to a backend large language
174+
model, passing block-specific context such as a video transcript identifier
175+
or a problem's siblings. A single aside class, registered as one entry
176+
point, handles both block types and uses ``should_apply_to_block`` to gate
177+
on a course-level waffle flag and per-course settings.
178+
179+
Thumbs Sample Aside
180+
===================
181+
182+
The `xblock-sdk`_ repository contains a ``ThumbsAside`` class in
183+
``sample_xblocks/thumbs/thumbs.py``. It is **not functional** and is not
184+
registered through any entry point. The class comment in the source notes:
185+
"Asides aren't ready yet, so this is currently not being installed in
186+
setup.py." It exists as a syntactic example of the decorator pattern, not
187+
as a working aside. Treat it as illustrative only.
188+
189+
Limitations
190+
***********
191+
192+
Asides are a real, working feature in production deployments, but the
193+
ecosystem around them is incomplete. The list below is drawn from the
194+
state of the codebase as of the Sumac release and from a 2025 Open edX
195+
Conference talk by Peter Pinch of MIT Open Learning. Read it before
196+
committing to an aside-based design.
197+
198+
No Authoring Story in the Course Authoring MFE
199+
==============================================
200+
201+
The Studio author view for an aside is rendered by the legacy course
202+
authoring frontend. The current Course Authoring micro-frontend has no
203+
defined location to display aside author UI. If your project depends on the
204+
new MFE for authoring, plan to render the aside's author UI through a
205+
different mechanism, or accept that authors will use the legacy Studio for
206+
this part of the workflow.
207+
208+
Not All XBlocks Round-Trip Through OLX
209+
======================================
210+
211+
OLX export and import for asides depends on the host XBlock cooperating
212+
with the export process. Some XBlocks, including ORA2, do not preserve
213+
aside data through their export and import paths. If your aside must
214+
survive a course export and re-import on a course that uses one of these
215+
blocks, test the round trip end to end before depending on it.
216+
217+
Multiple Asides on a Single Block Are Not Reliable
218+
==================================================
219+
220+
The runtime supports multiple aside types decorating the same block in
221+
principle, but interactions between asides on the same block are not
222+
well-tested. Two asides that both decorate ``student_view`` on the same
223+
block may render correctly in isolation and break when combined. If you
224+
need this, build a single aside that composes both behaviors rather than
225+
relying on two independent asides to coexist.
226+
227+
JavaScript Library Loading Is Limited
228+
=====================================
229+
230+
Asides use the same fragment-based JavaScript loading mechanism as XBlocks,
231+
which assumes a single set of static assets. If your aside needs a JS
232+
library that is not already loaded by the host page, you must add it
233+
through the fragment, and you must handle ordering and conflicts yourself.
234+
There is no shared aside-level mechanism for declaring library dependencies.
235+
236+
Documentation Has Historically Been Sparse
237+
==========================================
238+
239+
XBlock Asides have been part of the platform for years but have had no
240+
user-facing documentation until this set of articles. The original work was
241+
done by Dave Ormsbee. Most of the institutional knowledge has lived in
242+
docstrings, test code, and the implementations of a handful of asides
243+
maintained outside the core platform. If you find this documentation lacks
244+
detail your project needs, the test file at ``xblock/test/test_asides.py``
245+
in the XBlock repository is the most reliable source of behavioral truth.
246+
247+
Where to Go Next
248+
****************
249+
250+
If you are ready to build an aside, start with
251+
:ref:`XBlock Aside Quickstart`. If you already have a target XBlock in mind
252+
and want a step-by-step recipe, read :ref:`Add an XBlock Aside`. For the
253+
complete list of classes, decorators, methods, and entry points, consult
254+
:ref:`XBlock Asides Reference`.
255+
256+
.. _rapid-response-xblock: https://github.com/mitodl/rapid-response-xblock
257+
.. _ol-openedx-chat: https://github.com/mitodl/open-edx-plugins/tree/main/src/ol_openedx_chat
258+
.. _xblock-sdk: https://github.com/openedx/xblock-sdk
259+
260+
.. seealso::
261+
262+
:ref:`XBlock Asides Reference` (reference)
263+
The complete API surface for ``XBlockAside`` and its runtime hooks.
264+
265+
:ref:`Add an XBlock Aside` (how-to)
266+
A step-by-step recipe for adding an aside to existing XBlocks.
267+
268+
:ref:`XBlock Aside Quickstart` (quickstart)
269+
A beginner-friendly walkthrough from zero to a running aside.
270+
271+
:ref:`Hooks Extension Framework` (concept)
272+
An alternative extension mechanism for non-view-based behaviors.
273+
274+
**Maintenance chart**
275+
276+
+--------------+-------------------------------+----------------+--------------------------------+
277+
| Review Date | Working Group Reviewer | Release |Test situation |
278+
+--------------+-------------------------------+----------------+--------------------------------+
279+
| | | | |
280+
+--------------+-------------------------------+----------------+--------------------------------+

0 commit comments

Comments
 (0)