Using Vue in Markdown #
KawaPress compiles every Markdown page into a Vue Single-File Component. Markdown provides the document structure, while Vue template syntax, SFC blocks, and local components add dynamic behavior when needed.
SSR Compatibility
Dynamic page code participates in both server rendering and browser hydration. Read SSR Compatibility before accessing browser APIs or using client-only dependencies.
Template Syntax #
Interpolation #
Double curly braces in prose are evaluated as Vue expressions:
2 + 2 = **{{ 2 + 2 }}**The rendered result is: 2 + 2 = 4.
Keep interpolations short and free of side effects. Move state and complex logic into <script setup> so the document remains easy to read.
Directives #
Markdown accepts raw HTML with Vue directives:
<span v-for="number in 3" :key="number">{{ number }}</span>The rendered result is: 123.
Directives such as v-if, :class, and @click follow normal Vue template rules.
Install Vue #
A Markdown-only site using nagi only needs kawapress. Install Vue as a direct development dependency when you write Vue components or import reactive APIs from vue:
npm install --save-dev vuepnpm add --save-dev vueyarn add --dev vueThis gives the package manager and editor an explicit Vue dependency for your site code.
<script> and <style> #
Markdown pages support root-level <script>, <script setup>, and <style> blocks. Place them after frontmatter. Do not add a <template> block; all remaining Markdown content becomes the component template.
This page example manages state with <script setup> and applies page-local styles through a CSS Module:
---
title: Counter
---
<script setup lang="ts">
import { computed, shallowRef } from 'vue'
const count = shallowRef(1)
const doubled = computed(() => count.value * 2)
</script>
# Counter
<p :class="$style.summary">
Count: {{ count }}. Doubled: {{ doubled }}.
</p>
<button type="button" @click="count++">Increment</button>
<style module>
.summary {
font-weight: 600;
}
</style>Prefer <style module> for page-local Markdown styles. Unlike <style scoped>, it does not add a scope attribute to every element generated by the page. Put styles shared by multiple pages in a theme or Runtime Plugin CSS entry.
Use Components #
Import a Local Component #
Assume the site has a counter component next to its guide pages:
components/
└─ Counter.vue
guide/
└─ interactive.mdWrite components/Counter.vue as a standard Vue 3 SFC:
<script setup lang="ts">
import { shallowRef } from 'vue'
const count = shallowRef(0)
</script>
<template>
<button class="counter" type="button" @click="count++">
Clicks: {{ count }}
</button>
</template>
<style scoped>
.counter {
padding: 0.5rem 0.75rem;
color: inherit;
font: inherit;
cursor: pointer;
background: transparent;
border: 1px solid currentcolor;
border-radius: 0.5rem;
}
</style>Import it explicitly from guide/interactive.md with <script setup>:
<script setup lang="ts">
import Counter from '../components/Counter.vue'
</script>
# Interactive Example
<Counter />Import paths are relative to the Markdown file. Props, events, and slots follow normal Vue rules. Use PascalCase component names such as <Counter />.
Local imports preserve page-level code splitting, so the component loads only when its page is visited. KawaPress does not auto-register a component directory. Register a component through a Runtime Plugin only when most pages need it globally.
Access Current Page Data #
Import usePageData() from kawapress/client to read the current page title, path, frontmatter, and heading list:
<script setup lang="ts">
import { usePageData } from 'kawapress/client'
const page = usePageData()
</script>
Current page title: {{ page?.title }}usePageData() updates during client-side navigation. KawaPress does not send the entire site's page content to the browser.
Escape Vue Syntax #
Wrap content in v-pre when curly braces should remain literal:
<span v-pre>{{ this is not evaluated }}</span>The rendered result is: {{ this is not evaluated }}.
Fenced code blocks are protected by default, so Vue expressions inside them are not evaluated and do not need an extra v-pre.

