
TypeScript Beyond Basics
} -->
I recently built LiteBuilder - a simple yet powerful live code editor using Vue 3 and Tailwind CSS. It supports syntax highlighting, tag suggestions, live preview, file import/export, and even Tailwind-based styling.
All of it runs directly in the browser, with zero heavy dependencies - everything is custom-built, including the editor core using a plain <textarea>
.
<textarea>
.txt
files to continue editing laterLiteBuilder uses a clever trick - a hidden <pre>
element mirrors the <textarea>
content and applies syntax highlighting through custom parsing.
Here’s the basic setup:
<template>
<div class="relative font-mono">
<pre class="highlight-layer" v-html="highlightedCode"></pre>
<textarea v-model="rawCode" @input="onInput" class="editor-layer"></textarea>
</div>
</template>
Inside highlightedCode
, the text is parsed and injected as HTML:
function highlightHTML(raw: string): string {
return raw
.replace(/(<\/?[a-z-]+>?)/gi, '<span class="text-red-500">$1</span>')
.replace(/(".*?")/g, '<span class="text-green-500">$1</span>')
}
This function is used to keep a synchronized, styled preview of what you’re typing.
As the user types inside the editor, LiteBuilder detects open brackets (<
) and displays a suggestion list:
function getTagSuggestions(currentText: string): string[] {
const tags = ['div', 'span', 'button', 'input', 'p', 'a', 'img']
return tags.filter(tag => tag.startsWith(currentText))
}
Suggestions are rendered in an absolutely positioned list over the editor.
Saving is handled using Blob and a simple download trigger:
function downloadFile(content: string) {
const blob = new Blob([content], { type: 'text/plain' })
const a = document.createElement('a')
a.href = URL.createObjectURL(blob)
a.download = 'litebuilder.txt'
a.click()
}
And file input is used for importing:
<input type="file" accept=".txt" @change="onFileUpload" />
The live preview is rendered in an <iframe>
sandbox. Content is injected like so:
iframe.contentDocument!.body.innerHTML = `
<div class="prose">${rawCode.value}</div>
`
Tailwind is preloaded into the iframe context - which allows users to apply Tailwind classes live and immediately see the result.
For formatting, the editor uses a simple parser that ensures consistent indentation and spacing. You can trigger it with a button click:
function formatCode(raw: string): string {
return html_beautify(raw, { indent_size: 2 })
}
I wanted full control over the editing experience - without relying on big libraries like CodeMirror or Monaco. I also wanted to focus on:
minimal UI,
fast feedback loop (live preview),
and full offline usability.
This is especially useful for:
testing Tailwind layouts,
prototyping static HTML,
or teaching the basics of HTML structure and semantics.
LiteBuilder was a fun project that pushed me to work closer with raw DOM APIs and Vue reactivity without relying on external editors. It’s a great reminder that with the right mindset, you can build tools yourself - even the ones you’re used to importing as packages.
Bartłomiej Nowak
Programmer
Programmer focused on performance, simplicity, and good architecture. I enjoy working with modern JavaScript, TypeScript, and backend logic — building tools that scale and make sense.