Inline Components
Embed custom Vue components directly into your markdown card content using {{ <component-name /> }} syntax. This is a middle ground between basic fill-in syntax and fully custom cards.
Basic Example
The simplest inline component takes no parameters:
Here, badge is a Vue component that renders a styled span. The syntax {{ <badge /> }} tells the markdown renderer to insert the component inline.
Components with Data
Pass data to components using attributes:
The color and text attributes become props on the Vue component. This lets you reuse the same component with different data across many cards.
Real-World Example: Chess Positions
A chess position component that accepts FEN notation:
Notice how the chess component and fill-in blank {{ Nxe5 }} coexist in the same card. Multiple props work too:
Setting Up Components
Register components when bootstrapping your Vue app (in src/main.ts):
import { markRaw } from 'vue';
import Badge from './components/Badge.vue';
import ChessPosition from './components/ChessPosition.vue';
app.provide('markdownComponents', {
badge: markRaw(Badge),
chessPosition: markRaw(ChessPosition),
});Why markRaw()?
Use markRaw() for performance - it prevents Vue from making components reactive, which isn't needed here.
Components can be defined inline (like in this page's examples) or imported from .vue files. The markdown syntax stays the same either way.
Interactive Components (Grading)
Components can participate in answer evaluation by extending BaseUserInput:
<script lang="ts">
import BaseUserInput from '@vue-skuilder/common-ui/components/studentInputs/BaseUserInput';
export default {
extends: BaseUserInput,
props: ['expectedAnswer'],
methods: {
handleSubmit(value) {
// When user provides answer, submit for grading
this.submit({ userValue: value });
}
}
}
</script>How grading works:
- Component calls
this.submit(answer)with user's response - BaseUserInput walks up the component tree to find QuestionView ancestor
- QuestionView calls
Question.evaluate(answer, timeSpent) - Returns
Evaluation: { isCorrect: boolean, performance: number (0-1) }
See the fill-in implementation for a complete example:
- Component:
packages/common-ui/src/components/studentInputs/fillInInput.vue - Question class:
packages/courseware/src/default/questions/fillIn/index.ts
Key types (from @vue-skuilder/common):
Answer- Base interface for user responsesEvaluation-{ isCorrect: boolean, performance: number }
Key classes (from @vue-skuilder/common-ui):
BaseUserInput- Base component withsubmit()methodQuestion- Abstract class withisCorrect()andevaluate()methods
When to Use What?
{{ }}fill-in syntax → Simple text input or multiple choice- Inline components (this page) → Custom UI reused across many cards
- Full custom cards → Complete control over card logic, data model, and views