<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[My first vue markdown: What went wrong]]></title><description><![CDATA[My first vue markdown: What went wrong]]></description><link>https://my-first-vue-markdown-what-went-wrong.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 09 Jun 2026 16:48:03 GMT</lastBuildDate><atom:link href="https://my-first-vue-markdown-what-went-wrong.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Building My First Vue Markdown Editor: What Actually Went Wrong (and What I Learned)]]></title><description><![CDATA[Introduction
Firstly I am glad to be learning tech at Altschool Africa, I am also very much glad that altschhol equip us by ensuring we have projects and assessment to test what we have been learning. The pushing is real. After learning Vuejs I wante...]]></description><link>https://my-first-vue-markdown-what-went-wrong.hashnode.dev/building-my-first-vue-markdown-editor-what-actually-went-wrong-and-what-i-learned</link><guid isPermaLink="true">https://my-first-vue-markdown-what-went-wrong.hashnode.dev/building-my-first-vue-markdown-editor-what-actually-went-wrong-and-what-i-learned</guid><category><![CDATA[Frontend Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Vue.js]]></category><category><![CDATA[vue]]></category><category><![CDATA[React]]></category><category><![CDATA[buildinginpublic]]></category><category><![CDATA[frontend]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Ayomiposi Joshua George]]></dc:creator><pubDate>Sat, 10 Jan 2026 16:38:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768051236183/7cf3ad60-988f-41cb-9cf3-fc9413e24c5d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Firstly I am glad to be learning tech at <a target="_blank" href="https://altschoolafrica.com/">Altschool Africa</a>, I am also very much glad that altschhol equip us by ensuring we have projects and assessment to test what we have been learning. The pushing is real. After learning Vuejs I wanted to build something real, something that would force me to struggle, make mistakes, and actually understand what I was doing. Then Altschool gave us an assessment during the holiday to build a markdown… Lol.</p>
<p>A markdown editor felt like the right choice.</p>
<p>It wasn’t too simple, but it also wasn’t something I’d abandon halfway through.</p>
<p>What I didn’t expect was how many <strong>r</strong>eal development problems I’d run into. styling issues, Git errors, confusing Vue concepts, and moments where nothing worked and I didn’t even know why.</p>
<p>This article isn’t a perfect tutorial.</p>
<p>It’s an honest account of what building my first Vue project actually looked like.</p>
<h2 id="heading-why-i-built-the-markdown-editor">Why I Built The Markdown Editor</h2>
<p>Firstly it was a semester assessment project also like many developers learning frontend frameworks, I wanted a project that:</p>
<ul>
<li><p>Used real state management</p>
</li>
<li><p>Had live updates</p>
</li>
<li><p>Involved user input</p>
</li>
<li><p>Was something I could actually show to others</p>
</li>
</ul>
<p>A markdown editor checked all those boxes.</p>
<p>I also wanted to practice <a target="_blank" href="https://vuejs.org/">Vue 3</a>, <a target="_blank" href="https://www.typescriptlang.org/docs/">TypeScript</a>, and <a target="_blank" href="https://tailwindcss.com/docs/installation/using-vite">Tailwind CSS</a> together instead of learning them in isolation.</p>
<h2 id="heading-the-tech-stack-i-chose-and-why">The Tech Stack I Chose (and Why)</h2>
<p>Here’s what I used:</p>
<ul>
<li><p><strong>Vue 3 (Composition API)</strong>: because it’s the modern Vue standard</p>
</li>
<li><p><strong>TypeScript</strong>: to catch mistakes early</p>
</li>
<li><p><strong>Tailwind CSS</strong>: for fast styling</p>
</li>
<li><p><strong>Bun</strong>: mostly out of curiosity (and speed) also recommended by my instructor Mr Setemi Ojo</p>
</li>
<li><p><strong>marked</strong>: to convert markdown to HTML</p>
</li>
<li><p><strong>DOMPurify</strong>: to keep things secure</p>
</li>
</ul>
<p>Looking back, this stack taught me a lot, not just about Vue, but about how tools interact with each other.</p>
<h2 id="heading-how-the-app-is-structured-simple-but-powerful">How the App Is Structured (Simple but Powerful)</h2>
<p>The entire app is built around <strong>three components</strong>.</p>
<h3 id="heading-appvue-the-parent-that-holds-everything-together">App.vue: The Parent That Holds Everything Together</h3>
<p>At the top level, I had just one piece of state:</p>
<pre><code class="lang-plaintext">const markedText = ref("");
</code></pre>
<p>This single ref holds the markdown text. Whenever it changes, Vue automatically updates both the editor and the preview.</p>
<p>This was my first real “aha” moment with Vue’s reactivity. No extra libraries. Just ref.</p>
<h3 id="heading-editorvue-me-understanding-v-model">Editor.vue: Me Understanding v-model</h3>
<p>This component confused me at first.</p>
<pre><code class="lang-plaintext">const props = defineProps&lt;{ modelValue: string }&gt;();

const emit = defineEmits&lt;{
(e: "update:modelValue", value: string): void;
}&gt;();
</code></pre>
<p>I kept asking myself, “Why do I need both modelValue and update:modelValue?” Then it clicked. Props flow down while events flow up. v-model is just Vue helping me manage both directions cleanly.</p>
<p>Once I understood that, input handling stopped feeling magical and started making sense.</p>
<h3 id="heading-previewvue-where-everything-comes-together">Preview.vue: Where Everything Comes Together</h3>
<p>This is where the markdown is rendered in real time.</p>
<pre><code class="lang-plaintext">const renderedMarkdown = computed(() =&gt; {
const rawHtml = marked.parse(props.markedText);
return DOMPurify.sanitize(rawHtml as string);
});
</code></pre>
<p>This single block taught me three important lessons:</p>
<ul>
<li><p>Lesson 1: Computed properties matter. Using computed means Vue only recalculates when the markdown text changes. If I had used a normal function, it would run on every render. This was my introduction to performance aware thinking, even in small apps.</p>
</li>
<li><p>Lesson 2: Security is not Optional. Rendering markdown requires v-html, which can be dangerous. Without sanitization, users could inject malicious scripts. Using DOMPurify wasn’t optional—it was necessary.</p>
</li>
<li><p>Lesson 3: Lifecycle Hooks Prevent Hidden Bugs I added a real-time clock to the preview header. <strong>My first version worked… but it had a memory leak.</strong></p>
</li>
<li><pre><code class="lang-plaintext">    // ❌ Bad
    setInterval(() =&gt; {
    time.value = new Date().toLocaleTimeString();
    }, 1000);
    The interval kept running even when the component was destroyed.
</code></pre>
<p>  In the code above, the interval kept running even when the component was destroyed. The fix:</p>
</li>
<li><pre><code class="lang-plaintext">    let interval: ReturnType&lt;typeof setInterval&gt;;

    onMounted(() =&gt; {
    interval = setInterval(() =&gt; {
    time.value = new Date().toLocaleTimeString();
    }, 1000);
    });

    onUnmounted(() =&gt; {
    clearInterval(interval);
    });
</code></pre>
<p>  This experience taught me that things can work and still be wrong.</p>
</li>
</ul>
<h2 id="heading-the-struggles-that-nearly-slowed-me-down">The Struggles That Nearly Slowed Me Down</h2>
<p>Tailwind Was Completely Broken At one point, no styles applied at all.</p>
<p>After hours of confusion, I realized I was mixing @tailwindcss/vite plugin and traditional PostCSS setup</p>
<p>They conflicted. The fix for me was going back to the stable approach:</p>
<pre><code class="lang-plaintext">export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
</code></pre>
<h2 id="heading-what-this-project-really-taught-me">What This Project Really Taught Me</h2>
<ul>
<li><p>Vue’s reactivity is simpler than I expected</p>
</li>
<li><p>TypeScript forces better thinking</p>
</li>
<li><p>Cleaning up side effects matters</p>
</li>
<li><p>Documentation saves hours</p>
</li>
<li><p>Restarting is sometimes faster than debugging</p>
</li>
<li><p>Writing about what you learn helps it stick</p>
</li>
</ul>
<h2 id="heading-a-checklist-i-now-think-about-while-coding">A Checklist I Now Think About While Coding</h2>
<ul>
<li><p>Does this component do one thing?</p>
</li>
<li><p>Am I cleaning up side effects?</p>
</li>
<li><p>Is user input safe?</p>
</li>
<li><p>Are names clear?</p>
</li>
<li><p>Would someone else understand this?</p>
</li>
<li><p>Is this the simplest working solution?</p>
</li>
</ul>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Three weeks ago, I didn’t know Vue. Now I have a working markdown editor with real-time updates, clean architecture, type safety and security considerations. Am I an expert? No. Am I learning properly? Yes. The gap between learning and being job-ready is filled with projects like this; projects where things break, where one get stuck, where one ask questions, and slowly figure things out.</p>
<p>That’s not failure, that’s progress and I’m glad to be living my progress gradually.</p>
<p>🔗 Project Link GitHub: <a target="_blank" href="http://github.com/Ayomiposy/vue_markdown">github.com/Ayomiposy/vue_markdown</a></p>
]]></content:encoded></item></channel></rss>