Use Kria Lite WYSIWYG Editor in Vue
Learn how to integrate the lightweight Kria Lite WYSIWYG editor into your Vue.js application. This guide covers Vue 3 Composition API, Options API, and TypeScript support.
📋 Prerequisites
- ✓ Vue 3.x (Vue 2 with minor adjustments)
- ✓ Node.js and npm/yarn installed
- ✓ Basic understanding of Vue components
Installation
Install Kria Lite WYSIWYG editor using your preferred package manager:
# Using npm
npm install kria-lite
# Using yarn
yarn add kria-lite
# Using pnpm
pnpm add kria-lite
Vue 3 Composition API
Here's how to use Kria Lite with Vue 3's Composition API:
<!-- KriaEditor.vue -->
<template>
<div class="kria-editor">
<textarea ref="editorRef"></textarea>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { WYSIWYG } from 'kria-lite'
import 'kria-lite/dist/kria.editor.css'
const props = defineProps({
modelValue: {
type: String,
default: ''
},
placeholder: {
type: String,
default: 'Start writing...'
},
height: {
type: String,
default: '300px'
}
})
const emit = defineEmits(['update:modelValue'])
const editorRef = ref(null)
let editorInstance = null
onMounted(() => {
if (editorRef.value) {
editorInstance = WYSIWYG.init(editorRef.value, {
height: props.height,
placeholder: props.placeholder,
onChange: (content) => {
emit('update:modelValue', content)
}
})
// Set initial content
if (props.modelValue) {
editorInstance.setContent(props.modelValue)
}
}
})
onBeforeUnmount(() => {
if (editorInstance) {
editorInstance.destroy()
editorInstance = null
}
})
// Expose methods for parent components
defineExpose({
getContent: () => editorInstance?.getContent(),
setContent: (html) => editorInstance?.setContent(html),
focus: () => editorInstance?.focus()
})
</script>
Use the component with v-model:
<template>
<div>
<KriaEditor v-model="content" placeholder="Write your post..." />
<button @click="save">Save</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import KriaEditor from './components/KriaEditor.vue'
const content = ref('<p>Initial content</p>')
function save() {
console.log('Saving:', content.value)
// Save to your backend
}
</script>
Options API (Vue 2/3)
If you prefer the Options API or need Vue 2 compatibility:
<!-- KriaEditor.vue -->
<template>
<div class="kria-editor">
<textarea ref="editor"></textarea>
</div>
</template>
<script>
import { WYSIWYG } from 'kria-lite'
import 'kria-lite/dist/kria.editor.css'
export default {
name: 'KriaEditor',
props: {
value: {
type: String,
default: ''
},
placeholder: {
type: String,
default: 'Start writing...'
}
},
data() {
return {
editorInstance: null
}
},
mounted() {
this.editorInstance = WYSIWYG.init(this.$refs.editor, {
placeholder: this.placeholder,
onChange: (content) => {
this.$emit('input', content)
}
})
if (this.value) {
this.editorInstance.setContent(this.value)
}
},
beforeDestroy() {
if (this.editorInstance) {
this.editorInstance.destroy()
}
},
watch: {
value(newVal) {
const currentContent = this.editorInstance?.getContent()
if (currentContent !== newVal) {
this.editorInstance?.setContent(newVal)
}
}
},
methods: {
getContent() {
return this.editorInstance?.getContent()
},
focus() {
this.editorInstance?.focus()
}
}
}
</script>
Reusable Composable
Create a reusable composable for the editor logic:
// composables/useKriaEditor.js
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { WYSIWYG } from 'kria-lite'
export function useKriaEditor(options = {}) {
const editorRef = ref(null)
let instance = null
onMounted(() => {
if (editorRef.value) {
instance = WYSIWYG.init(editorRef.value, {
height: '300px',
...options
})
}
})
onBeforeUnmount(() => {
instance?.destroy()
instance = null
})
const getContent = () => instance?.getContent() || ''
const setContent = (html) => instance?.setContent(html)
const focus = () => instance?.focus()
return {
editorRef,
getContent,
setContent,
focus
}
}
Use the composable:
<script setup>
import { useKriaEditor } from '@/composables/useKriaEditor'
const { editorRef, getContent, setContent } = useKriaEditor({
placeholder: 'Write something...',
imageUploadUrl: '/api/upload'
})
function handleSave() {
const content = getContent()
console.log('Content:', content)
}
</script>
<template>
<textarea ref="editorRef"></textarea>
<button @click="handleSave">Save</button>
</template>
Full v-model Support
A complete component with two-way binding and watchers:
<script setup>
import { ref, watch, onMounted, onBeforeUnmount } from 'vue'
import { WYSIWYG } from 'kria-lite'
const props = defineProps({
modelValue: String
})
const emit = defineEmits(['update:modelValue'])
const editorRef = ref(null)
let instance = null
let isInternalChange = false
onMounted(() => {
instance = WYSIWYG.init(editorRef.value, {
onChange: (content) => {
isInternalChange = true
emit('update:modelValue', content)
nextTick(() => {
isInternalChange = false
})
}
})
if (props.modelValue) {
instance.setContent(props.modelValue)
}
})
// Watch for external changes
watch(() => props.modelValue, (newVal) => {
if (!isInternalChange && instance) {
const current = instance.getContent()
if (current !== newVal) {
instance.setContent(newVal || '')
}
}
})
onBeforeUnmount(() => {
instance?.destroy()
})
</script>
TypeScript Support
Fully typed composable for TypeScript projects:
// composables/useKriaEditor.ts
import { ref, onMounted, onBeforeUnmount, Ref } from 'vue'
import { WYSIWYG, EditorOptions, EditorInstance } from 'kria-lite'
interface UseKriaEditorReturn {
editorRef: Ref<HTMLTextAreaElement | null>
getContent: () => string
setContent: (html: string) => void
focus: () => void
}
export function useKriaEditor(
options: Partial<EditorOptions> = {}
): UseKriaEditorReturn {
const editorRef = ref<HTMLTextAreaElement | null>(null)
let instance: EditorInstance | null = null
onMounted(() => {
if (editorRef.value) {
instance = WYSIWYG.init(editorRef.value, options)
}
})
onBeforeUnmount(() => {
instance?.destroy()
instance = null
})
return {
editorRef,
getContent: () => instance?.getContent() ?? '',
setContent: (html: string) => instance?.setContent(html),
focus: () => instance?.focus()
}
}
Nuxt.js Integration
For Nuxt.js, use client-only rendering since Kria Lite requires DOM:
<!-- components/KriaEditor.client.vue -->
<!-- The .client.vue suffix ensures client-only rendering in Nuxt 3 -->
<template>
<textarea ref="editorRef"></textarea>
</template>
<script setup>
import { useKriaEditor } from '@/composables/useKriaEditor'
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const { editorRef } = useKriaEditor({
onChange: (content) => emit('update:modelValue', content)
})
</script>
Or use the ClientOnly wrapper:
<template>
<ClientOnly>
<KriaEditor v-model="content" />
<template #fallback>
<div class="h-[300px] bg-gray-100 animate-pulse"></div>
</template>
</ClientOnly>
</template>
Best Practices
✅ Do: Use onBeforeUnmount for cleanup
Always destroy the editor instance to prevent memory leaks, especially in SPAs where components mount/unmount frequently.
✅ Do: Lazy load the editor
Use defineAsyncComponent to
load the editor only when needed.
❌ Don't: Initialize in reactive
Don't make the editor instance reactive (ref/reactive). Store it as a plain variable to avoid unnecessary re-renders.
Next Steps
You now have Kria Lite WYSIWYG editor running in your Vue.js application!