Framework Integration
quikdown is framework-agnostic. No wrapper package needed โ just import and use.
Framework Integration
quikdown works in any framework. The parser is a pure function (markdown in, HTML out). The editor manages its own DOM, so you mount it in a lifecycle hook and clean it up on unmount. Here are copy-paste patterns for the most common frameworks.
React hooks
Works with React 18+. The parser uses useMemo, the editor uses useEffect + useRef.
Parser only
Render markdown as HTML with dangerouslySetInnerHTML. quikdown is XSS-safe by default.
import { useMemo } from 'react';
import quikdown from 'quikdown';
export function Markdown({ source }) {
const html = useMemo(
() => quikdown(source), [source]
);
return <div dangerouslySetInnerHTML=
{{ __html: html }} />;
}
Full editor component
Mount once in useEffect, sync props, destroy on unmount.
import { useEffect, useRef } from 'react';
import QuikdownEditor from 'quikdown/edit';
export function Editor({ value, onChange }) {
const el = useRef(null);
const ed = useRef(null);
useEffect(() => {
ed.current = new QuikdownEditor(
el.current,
{ mode: 'split', showUndoRedo: true,
onChange: (md) => onChange?.(md) }
);
ed.current.setMarkdown(value || '');
return () => ed.current.destroy();
}, []);
useEffect(() => {
if (ed.current &&
value !== ed.current.getMarkdown())
ed.current.setMarkdown(value || '');
}, [value]);
return <div ref={el}
style={{ height: 500 }} />;
}
SSR / Next.js
The parser works server-side. The editor needs DOM โ use dynamic(() => import('quikdown/edit'), { ssr: false }).
Vue 3 composition API
Uses computed + v-html for the parser, onMounted + ref for the editor.
Parser only
<script setup>
import { computed } from 'vue';
import quikdown from 'quikdown';
const props = defineProps({
source: String
});
const html = computed(
() => quikdown(props.source)
);
</script>
<template>
<div v-html="html"></div>
</template>
Full editor component
<script setup>
import { ref, onMounted,
onBeforeUnmount, watch } from 'vue';
import QuikdownEditor from 'quikdown/edit';
const props = defineProps({
modelValue: { type: String, default: '' },
mode: { type: String, default: 'split' },
theme: { type: String, default: 'auto' },
});
const emit = defineEmits(
['update:modelValue']
);
const el = ref(null);
let editor = null;
onMounted(() => {
editor = new QuikdownEditor(el.value, {
mode: props.mode, theme: props.theme,
showUndoRedo: true,
onChange: (md) =>
emit('update:modelValue', md),
});
editor.setMarkdown(props.modelValue);
});
onBeforeUnmount(() => editor?.destroy());
watch(() => props.modelValue, (v) => {
if (editor && v !== editor.getMarkdown())
editor.setMarkdown(v);
});
</script>
<template>
<div ref="el" style="height: 500px" />
</template>
Nuxt SSR
Import the editor inside onMounted with await import('quikdown/edit'). The parser is safe server-side.
Svelte 5 / 4
Use onMount / onDestroy for the editor, reactive statements for the parser.
Parser only
<script>
import quikdown from 'quikdown';
export let source = '';
$: html = quikdown(source);
</script>
{@html html}
Full editor component
<script>
import { onMount, onDestroy } from 'svelte';
import QuikdownEditor from 'quikdown/edit';
export let value = '';
export let onChange = () => {};
let el;
let editor;
onMount(() => {
editor = new QuikdownEditor(el, {
mode: 'split', showUndoRedo: true,
onChange: (md) => onChange(md),
});
editor.setMarkdown(value);
});
onDestroy(() => editor?.destroy());
$: if (editor &&
value !== editor.getMarkdown())
editor.setMarkdown(value);
</script>
<div bind:this={el}
style="height: 500px" />
SvelteKit SSR
Guard the editor import: if (browser) { const { default: QE } = await import('quikdown/edit'); ... }
Angular standalone component
Use ngAfterViewInit to mount, ngOnDestroy to clean up.
Parser only
import { Component, Input } from
'@angular/core';
import { DomSanitizer } from
'@angular/platform-browser';
import quikdown from 'quikdown';
@Component({
selector: 'app-markdown',
template: `<div
[innerHTML]="safeHtml"></div>`,
standalone: true,
})
export class MarkdownComponent {
@Input() source = '';
get safeHtml() {
return this.sanitizer
.bypassSecurityTrustHtml(
quikdown(this.source));
}
constructor(
private sanitizer: DomSanitizer) {}
}
Full editor component
import { Component, ElementRef,
ViewChild, Input, Output,
EventEmitter, AfterViewInit,
OnDestroy } from '@angular/core';
import QuikdownEditor from 'quikdown/edit';
@Component({
selector: 'app-editor',
template: `<div #container
style="height:500px"></div>`,
standalone: true,
})
export class EditorComponent
implements AfterViewInit, OnDestroy {
@ViewChild('container')
containerRef!: ElementRef;
@Input() value = '';
@Output() valueChange =
new EventEmitter<string>();
private editor?: QuikdownEditor;
ngAfterViewInit() {
this.editor = new QuikdownEditor(
this.containerRef.nativeElement, {
mode: 'split', showUndoRedo: true,
onChange: (md) =>
this.valueChange.emit(md),
});
this.editor.setMarkdown(this.value);
}
ngOnDestroy() {
this.editor?.destroy();
}
}
Angular Universal SSR
Check isPlatformBrowser(this.platformId) before creating the editor.
Vanilla JS / Web Components
No framework needed. One script tag or import.
Script tag
<div id="editor"
style="height: 500px"></div>
<script type="module">
import QuikdownEditor from
'https://unpkg.com/quikdown/dist/quikdown_edit.esm.min.js';
new QuikdownEditor('#editor', {
mode: 'split',
showUndoRedo: true,
});
</script>
Web Component wrapper
import QuikdownEditor from 'quikdown/edit';
class QdEditor extends HTMLElement {
connectedCallback() {
this.editor = new QuikdownEditor(
this,
{ mode: 'split', showUndoRedo: true }
);
}
disconnectedCallback() {
this.editor?.destroy();
}
}
customElements.define(
'qd-editor', QdEditor
);