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
);