Markdown integration
The default Aurelia 2 starter uses Vite, which can import plaintext assets with the built-in ?raw query string. That makes it easy to store content in .md files, load the raw Markdown at build time, and render it with a library such as markdown-it before handing it to Aurelia's templating system.
1. Install the Markdown toolchain
npm install markdown-it dompurifymarkdown-itconverts Markdown into HTML on the client with support for GFM tables, autolinks, etc.dompurifysanitizes the rendered HTML so untrusted Markdown cannot inject scripts.
2. Tell TypeScript about ?raw Markdown imports
?raw Markdown importsAdd the following to src/env.d.ts so TypeScript understands import doc from './doc.md?raw':
declare module '*.md?raw' {
const content: string;
export default content;
}3. (Optional) Hint Vite about Markdown assets
If you keep Markdown outside src/, add an assetsInclude entry so Vite watches those files:
// vite.config.ts
import { defineConfig } from 'vite';
import aurelia from '@aurelia/vite-plugin';
export default defineConfig({
plugins: [aurelia()],
assetsInclude: ['**/*.md'],
});4. Create a reusable Markdown viewer
Component template
<!-- src/resources/markdown-viewer.html -->
<template>
<article class="markdown-body" innerhtml.bind="html"></article>
</template>Component logic
This example automatically loads files from src/content via import.meta.glob, converts them with markdown-it, and sanitizes the HTML before binding it to the DOM.
// src/resources/markdown-viewer.ts
import { bindable, customElement } from '@aurelia/runtime-html';
import MarkdownIt from 'markdown-it';
import DOMPurify from 'dompurify';
const markdownFiles = import.meta.glob('../content/**/*.md', { as: 'raw' });
const md = new MarkdownIt({ html: false, linkify: true, typographer: true });
import template from './markdown-viewer.html?raw';
@customElement({ name: 'markdown-viewer', template })
export class MarkdownViewer {
@bindable() src: string = 'about.md';
protected html = '';
binding() {
void this.load(this.src);
}
protected srcChanged(newValue: string) {
void this.load(newValue);
}
private async load(path: string | undefined) {
if (!path) {
this.html = '';
return;
}
const key = `../content/${path}`;
const loader = markdownFiles[key];
if (!loader) {
this.html = `<p>Cannot find ${path}</p>`;
return;
}
const raw = await loader();
const rendered = md.render(raw);
this.html = DOMPurify.sanitize(rendered);
}
}5. Author Markdown content
Place any .md files under src/content. For example:
<!-- src/content/about.md -->
# About
Aurelia renders *Markdown* via **markdown-viewer**.6. Use Markdown inside your views
<!-- src/components/my-app.html -->
<template>
<markdown-viewer src="about.md"></markdown-viewer>
</template>Troubleshooting & tips
Hot reload: Because the files are imported through Vite's module graph, edits to
.mdfiles trigger HMR automatically.Frontmatter: If you need YAML frontmatter, parse it in
load()(e.g., withgray-matter) before passing the Markdown body tomarkdown-it.Accessibility: Keep semantic headings in your Markdown so screen readers can navigate the generated article structure.
Security: Always sanitize rendered HTML before binding it with
innerhtml.bindwhen authors can edit Markdown content.
With this setup you can freely mix .html and .md files in your Aurelia project, while still leveraging Vite features such as import.meta.glob, lazy loading, and HMR.
Last updated
Was this helpful?