Quickstart: Build Your First XBlock Aside#
Build, install, and run a minimal XBlock Aside in a Tutor development environment. By the end of this quickstart, you will have an Aside that adds a small banner to every Problem block in a course, and you will understand the four moving parts every Aside has: the class, the decorated views, the entry point, and the install step.
This quickstart deliberately keeps the Aside trivial. For a more realistic recipe, work through Add an XBlock Aside next. For the conceptual background, see About XBlock Asides.
Prerequisites#
You need:
A running Tutor development environment with the LMS and Studio services accessible. Tutor’s Getting started guide walks through the install if you do not have one yet.
The installed Python version used by the target Open edX release.
A course in your devstack with at least one Problem block. The Tutor demo course works.
A text editor and a terminal.
You do not need:
Prior experience writing XBlocks. The Aside in this quickstart uses plain Python and a hardcoded HTML string.
Familiarity with the Open Learning XML (OLX) format.
Step 1: Create a Python package#
In a working directory of your choice, create the following file structure:
hello_aside/
├── pyproject.toml
└── hello_aside/
├── __init__.py
└── Aside.py
Make __init__.py an empty file. The remaining steps describe what to
put in the other two files.
Step 2: Write the Aside class#
In hello_aside/Aside.py, paste the following code:
from web_fragments.fragment import Fragment
from xblock.core import XBlockAside
class HelloAside(XBlockAside):
"""A trivial Aside that prints a banner above every Problem block."""
@XBlockAside.aside_for("student_view")
def student_view_aside(self, block, context=None):
return Fragment(
'<div style="padding:8px;background:#eef;border:1px solid #99c;">'
'Hello from an XBlock Aside!'
'</div>'
)
@classmethod
def should_apply_to_block(cls, block):
return getattr(block, "category", None) == "problem"
This is the entire implementation. Three things to notice:
The class subclasses
XBlockAside.The
aside_for()decorator marksstudent_view_asideas the method to call when an XBlock’sstudent_viewis being rendered.should_apply_to_block()restricts the Aside to Problem blocks. Without it, the banner would appear on every block in every course.
Step 3: Configure the package#
In hello_aside/pyproject.toml, paste:
[project]
name = "hello-Aside"
version = "0.1.0"
requires-python = ">=X.Y" # set to the minimum Python version for the target Open edX release
dependencies = ["XBlock", "web-fragments"]
[project.entry-points."xblock_asides.v1"]
hello_aside = "hello_aside.Aside:HelloAside"
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
The critical line is the one under
[project.entry-points."xblock_asides.v1"]. It tells the Open edX
platform that a class called HelloAside, in the hello_aside.Aside
module, should be loaded as an Aside under the type name
hello_aside. This entry point group is how every Aside is
discovered.
Step 4: Install the package into Tutor#
From the directory containing hello_aside/, mount the package into
Tutor and relaunch the development environment:
tutor mounts add lms,cms:./hello_aside:/openedx/hello_aside
tutor dev exec lms bash
pip install -e /openedx/hello_aside
exit
tutor dev restart lms
The mounts add command tells Tutor to mount the local package into
both the LMS and Studio containers each time they start.
If you are not using Tutor, install the package directly into the LMS
and Studio Python environments with pip install -e ./hello_aside,
then restart both services.
Step 5: Enable the Aside system in the LMS#
The LMS gates Aside rendering on a global Django configuration model,
XBlockAsidesConfig. Until that model is enabled, no Aside renders on
any block, regardless of whether it is registered or installed.
Open the LMS Django admin in your browser:
http://<your-lms-host>/admin/lms_xblock/xblockasidesconfig/
Click Add to create a new revision. Check Enabled and click Save. The new revision becomes the current configuration immediately.
The same form has a Disabled blocks field that holds a
space-separated list of block types on which asides will never
render. The default value is about course_info static_tab. The
hello_aside example targets problem blocks, which are not in the
default disabled list, so no further changes are needed.
Studio has its own, separate gate — a different model,
StudioConfig, controls whether asides render in Studio, and it
defaults to disabled too. This quickstart’s hello_aside only
decorates student_view, so it has nothing to show in Studio either
way; see Add an XBlock Aside for the Studio half of enabling
asides, and for adding an author-facing view in the first place.
Step 6: Verify the Aside is rendering#
Open a course in the LMS, navigate to a unit that contains a Problem block, and confirm a light blue banner reading “Hello from an XBlock Aside!” appears below the problem. If you see the banner, your Aside is working.
If the banner does not appear, work through these checks in order:
The Aside is registered. Open a Django shell and run:
from xblock.core import XBlockAside print(list(XBlockAside.load_classes()))
The list should include
hello_aside. If it does not, the entry point is not installed; recheck Step 3 and Step 4.The block type matches. Your test block must have
category == "problem". A Video block, an HTML block, or a Discussion block will not trigger the Aside.The Aside failed to load, not just to apply. If your Aside’s type name doesn’t even show up in step 1’s
load_classes()list, check the logs for a warning about failing to load it as a plugin — a broken import in your package is dropped silently at that stage, which is what actually makes a broken Aside look like a missing one. An exception raised after loading, insidestudent_view_asideorshould_apply_to_block, is a different situation and isn’t swallowed the same way — it renders as a visible error block in place of the banner, rather than no banner at all; see Add an XBlock Aside for how to tell the two apart.
What You Just Built#
You have a working Aside with all four required pieces:
- A class
HelloAsidesubclassesXBlockAside.- A decorated view
student_view_asideis decorated with@XBlockAside.aside_for("student_view"), so it is invoked whenever the runtime renders any block’sstudent_view.- A filter
should_apply_to_blockrestricts the Aside to Problem blocks.- An entry point
The
xblock_asides.v1entry point inpyproject.tomlmakes the Aside discoverable by the platform at startup.
Every production Aside has the same four pieces, plus additional features such as scoped fields, AJAX handlers, author-side UI, and template-rendered HTML.
Where to Go Next#
To turn this trivial example into something useful:
Add a course-author toggle. Declare a
Booleanfield withScope.settingsand conditionally render the banner based on its value. The Add an XBlock Aside how-to walks through this.Render from a template. Replace the inline HTML string with a template loaded from your package’s static assets, rendered through the runtime’s template service.
Add an AJAX handler. Decorate a method with
@XBlock.handlerand call it from JavaScript in your fragment to support interactive behavior.Decorate the author view. Add a second method decorated with
@XBlockAside.aside_for("author_view")to render an author-facing preview in Studio.
For each of these extensions, the XBlock Asides Reference is the authoritative source. For the trade-offs and current limitations of the Aside mechanism, read About XBlock Asides before scaling up your design.
See also
- About XBlock Asides (concept)
Why asides exist, what problem they solve, and current limitations.
- Add an XBlock Aside (how-to)
A step-by-step recipe for adding an Aside to existing XBlocks.
- XBlock Asides Reference (reference)
The complete API surface for
XBlockAsideand its runtime hooks.
Maintenance chart
Review Date |
Working Group Reviewer |
Release |
Test situation |