This document describes the custom MDX components available for creating interactive lesson content in the LMS.
Lesson content is stored in the database as MDX (Markdown + JSX) and rendered using next-mdx-remote-client. This allows teachers to create rich, interactive content using simple markdown syntax combined with custom React components.
Note: While MDX components remain the rendering foundation for lesson content, teachers primarily create content through the Block Editor — a visual editing interface that serializes to MDX. See the Block Editor section below for details.
Highlight important information with visual callouts.
<Callout type="info">
This is an informational message.
</Callout>
<Warning>
Be careful with this operation!
</Warning>
<Tip>
Pro tip: Use keyboard shortcuts to work faster.
</Tip>
<Success>
Great job! You've completed this section.
</Success>
<Danger>
This action cannot be undone.
</Danger>Props:
type:'info' | 'warning' | 'tip' | 'success' | 'danger'(default:'info')title: Optional custom title
Aliases: <Info>, <Warning>, <Tip>, <Success>, <Danger>
Hide content that students should try to figure out first.
<Spoiler label="Click to reveal the answer">
The answer is 42.
</Spoiler>
<Solution>
Here's the complete solution code...
</Solution>
<Hint>
Try thinking about this problem differently.
</Hint>
<Answer>
The correct answer is B.
</Answer>Props:
label: Text shown on the reveal button (default varies by type)defaultOpen: Start expanded (default:false)
Aliases: <Solution>, <Hint>, <Answer>
Interactive multiple-choice questions with instant feedback.
<Quiz
question="What is the capital of France?"
options={["London", "Paris", "Berlin", "Madrid"]}
correctIndex={1}
explanation="Paris has been the capital of France since the 10th century."
/>Props:
question: The question text (required)options: Array of answer options (required)correctIndex: Index of the correct answer, 0-based (required)explanation: Explanation shown after answering
Display code with syntax highlighting and copy button.
```javascript
function greet(name) {
return `Hello, ${name}!`;
}
```
<CodeBlock language="python" filename="example.py" showLineNumbers>
def greet(name):
return f"Hello, {name}!"
</CodeBlock>Props:
language: Programming language for syntax highlightingfilename: Optional filename to displayshowLineNumbers: Show line numbers (default:false)highlightLines: Array of line numbers to highlight
Standard markdown code fences are automatically converted to <CodeBlock>.
For language learning - display words with translations and audio.
<Vocabulary
word="Hola"
translation="Hello"
pronunciation="/ˈola/"
audioUrl="/audio/hola.mp3"
example="¡Hola! ¿Cómo estás?"
exampleTranslation="Hello! How are you?"
/>Props:
word: The word or phrase (required)translation: Translation in target language (required)pronunciation: IPA or phonetic pronunciationaudioUrl: URL to audio pronunciationexample: Example sentence using the wordexampleTranslation: Translation of the example
Display sequential instructions with numbered steps.
<Steps>
<Step title="Install dependencies">
Run `npm install` to install all required packages.
</Step>
<Step title="Configure environment">
Create a `.env.local` file with your settings.
</Step>
<Step title="Start development server">
Run `npm run dev` to start the server.
</Step>
</Steps>Props (Steps):
startFrom: Starting number (default:1)
Props (Step):
title: Step title (optional)
Display terms with their definitions.
<Definition term="API">
An Application Programming Interface is a set of protocols and tools
for building software applications.
</Definition>
<Glossary>
<Definition term="REST">
Representational State Transfer - an architectural style for APIs.
</Definition>
<Definition term="JSON">
JavaScript Object Notation - a lightweight data interchange format.
</Definition>
</Glossary>Props (Definition):
term: The term being defined (required)
Side-by-side comparisons for showing differences.
<Compare
left={{
title: "Before",
content: "The old way of doing things"
}}
right={{
title: "After",
content: "The new improved approach"
}}
/>
<CodeCompare
before={`const x = 1;`}
after={`const x: number = 1;`}
language="typescript"
beforeTitle="JavaScript"
afterTitle="TypeScript"
/>
<BeforeAfter
before="Confusing instructions that are hard to follow"
after="Clear, step-by-step guide that anyone can understand"
/>Props (Compare):
left:{ title?: string, content: ReactNode }right:{ title?: string, content: ReactNode }
Props (CodeCompare):
before: Code before changes (required)after: Code after changes (required)language: Programming languagebeforeTitle: Label for before (default: "Before")afterTitle: Label for after (default: "After")
Props (BeforeAfter):
before: Text/content before (required)after: Text/content after (required)beforeLabel: Label (default: "Before")afterLabel: Label (default: "After")
All standard markdown features are supported:
- Headings:
# H1,## H2,### H3, etc. - Emphasis:
*italic*,**bold**,***bold italic*** - Lists: Ordered and unordered
- Links:
[text](url) - Images:
 - Blockquotes:
> quote - Tables: GFM table syntax
- Code: Inline `code` and fenced code blocks
- Content is stored as MDX string in the
lessons.contentcolumn - On page load,
next-mdx-remote-clientcompiles the MDX on the client - The compiled MDX is rendered with custom components from
components/lesson/mdx-components.tsx
components/lesson/- All lesson MDX componentscomponents/lesson/index.ts- Barrel exportcomponents/lesson/mdx-components.tsx- Component map for MDXapp/[locale]/dashboard/student/courses/[courseId]/lessons/[lessonId]/lesson-content.tsx- Renderer
- Create component in
components/lesson/ - Export from
components/lesson/index.ts - Add to
lessonMdxComponentsincomponents/lesson/mdx-components.tsx
# Introduction to Variables
Variables are containers for storing data values.
<Tip>
Choose meaningful variable names that describe what the data represents.
</Tip>
## Declaring Variables
<Steps>
<Step title="Use const for constants">
Use `const` when the value won't change.
</Step>
<Step title="Use let for variables">
Use `let` when the value might change.
</Step>
</Steps>
```javascript
const PI = 3.14159;
let count = 0;<Quiz question="Which keyword should you use for a value that won't change?" options={["var", "let", "const", "variable"]} correctIndex={2} explanation="const is used for values that remain constant throughout the program." />
A named storage location in memory that holds a value which can be referenced and manipulated in a program. Here's the complete example:const greeting = "Hello";
let name = "World";
console.log(`${greeting}, ${name}!`);The Block Editor is the primary lesson editing interface for teachers. It provides a visual, drag-and-drop editing experience that wraps MDX content in a structured block-based editor.
Location: components/teacher/block-editor/
- Teachers create content using visual blocks in the editor
- Blocks are serialized to MDX via
serializer.ts - The resulting MDX is stored in
lessons.contentand rendered using the MDX components documented above
The block editor supports all standard MDX component types plus additional interactive block types:
| Block Type | Description |
|---|---|
| Text / Heading | Rich text and headings (standard MDX) |
| Code | Code blocks with syntax highlighting |
| Image / Video | Media embeds |
| Quiz | Interactive multiple-choice questions |
| Callout | Info/warning/tip/success/danger callouts |
| Steps | Sequential instruction blocks |
| Vocabulary | Language learning word cards |
| Spoiler / Solution | Collapsible content |
| Audio | Audio file playback blocks |
| Embed | External content embeds (YouTube, etc.) |
| File-Download | Downloadable file attachments |
| Flashcard-Set | Interactive flashcard decks |
| Fill-in-the-Blank | Cloze-style exercises |
| Matching-Pairs | Match items between two columns |
| Ordering | Drag-to-reorder exercises |
| Comparison | Side-by-side content comparison |
| Table | Structured data tables |
| Glossary | Term-definition collections |
The block types marked in bold go beyond the base MDX component set and are unique to the block editor experience.
| File | Purpose |
|---|---|
components/teacher/block-editor/block-editor.tsx |
Main editor component |
components/teacher/block-editor/serializer.ts |
Converts blocks to MDX string |
components/teacher/block-editor/blocks/ |
Individual block type components |
All components automatically support dark mode through Tailwind's dark: variant classes. No additional configuration is needed.
Components are built with accessibility in mind:
- Proper ARIA labels and roles
- Keyboard navigation support
- Screen reader announcements
- Sufficient color contrast