--- url: /guide.md --- # What is VueMarkik? **VueMarkik** (pronounced *view-mark-eek*, IPA /vjuː mɑːrk ik/) is a mini component library for Vue.js with components that take a markdown string and render it to Vue elements without `v-html`. It is built on top of [unified](https://github.com/unifiedjs/unified), using [remark](https://github.com/remarkjs/remark) to parse markdown, and [rehype](https://github.com/rehypejs/rehype) to render it to Vue components. Raw HTML is disabled, final HAST is sanitized after plugins, and unsafe URL protocols are filtered by default; see the [security guide](./security) for the complete trust boundary. Just want to use it? Skip to [Getting Started](./getting-started). Working with LLM output? See [Streaming Markdown](./streaming-markdown). The name **VueMarkik** comprises the words *Vue Markdown* with the Armenian diminutive *-ik* (*-իկ*), which turns nouns into smaller, cuter, or familiar versions of themselves. Thus **VueMarkik** is a small friendly plugin for rendering markdown in Vue.js. ## Features * **Markdown Extensions** VueMarkik can be configured with [remark plugins](https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins) to change how markdown is transformed, enabling support for markdown syntax extensions such as GFM and MDX. * **Custom Rendering** VueMarkik can be configured with [rehype plugins](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#list-of-plugins) to change how markdown is rendered, for example syntax highlighting with [Shiki](https://shiki.matsu.io/), diagrams with [mermaid](https://mermaid.js.org/), and math rendering with [KaTeX](https://katex.org/). * **Custom Vue Components** To further customize the rendered markdown, you can provide custom Vue components that will be used instead of standard HTML elements, or use template slots for dynamic customization. For example, a custom component or slot for `

` can customise the rendering of `# hola`. * **Streaming-Friendly Recovery** When markdown is updated incrementally, such as from LLM output, VueMarkik can preserve the last successful render and emit `render-error` instead of flooding the console with warnings. --- --- url: /guide/getting-started.md --- # Getting Started ## Installation Install using your package manager: ::: code-group ```sh [npm] npm install vuemarkik ``` ```sh [yarn] yarn add vuemarkik ``` ```sh [pnpm] pnpm add vuemarkik ``` ```sh [bun] bun add vuemarkik ``` ```sh [deno] deno add npm:vuemarkik ``` ::: ## Simple Rendering To render a markdown string, import the `Markdown` component and pass the string to the `text` prop: ```html ``` This renders the following: ::: raw ::: ## What's Next? Learn how to customize and extend VueMarkik: * **[Security](./security)** - Review the default URL policy and plugin trust boundary * **[Migrating to v2](./migrating-to-v2)** - Upgrade existing plugin integrations to the safe default * **[Custom Vue Components](./custom-vue-components)** - Use your own components or slots for rendering * **[Remark & Rehype Plugins](./remark-rehype-plugins)** - Extend markdown syntax and rendering * **[Streaming Markdown](./streaming-markdown)** - Handle incremental LLM output without console spam Or explore specific examples: * **[GitHub Flavored Markdown](./github-flavored-markdown)** - Tables, task lists, strikethrough, and more * **[Syntax Highlighting](./syntax-highlighting)** - Code highlighting with Shiki * **[Mermaid Diagrams](./mermaid)** - Render diagrams and flowcharts * **[Math with KaTeX](./katex)** - Display mathematical equations --- --- url: /guide/security.md --- # Security VueMarkik is safe by default for untrusted markdown under the boundary documented here. It keeps raw HTML disabled, runs user-configured remark and rehype plugins, sanitizes their final HAST output, applies a URL policy, and only then converts the tree to Vue VNodes without `v-html` or `dangerouslySetInnerHTML`. ## Safe mode `securityMode="safe"` is the default for `Markdown`, `MarkdownAsync`, and `MarkdownHooks`. The rendering pipeline is: ```text markdown -> remark parser and plugins -> remark-rehype -> rehype plugins -> final HAST sanitizer -> URL transform -> Vue VNodes ``` The final sanitizer uses `rehype-sanitize` with a GitHub-style allowlist. It: * Allows normal CommonMark and GFM elements and properties. * Removes scripting, embedded browsing contexts, event properties, inline styles, and unknown metadata. * Prefixes DOM-clobbering `id` and `name` values. * Restricts URL protocols. * Sanitizes the HAST node and properties passed to custom components and slots. The default URL policy allows relative, fragment, query, `http`, `https`, `mailto`, and `tel` URLs. Other explicit protocols, including `javascript`, `vbscript`, and `data`, are removed. ## Trusted mode Use trusted mode only when both the markdown and the configured plugins are trusted: ```vue ``` Trusted mode skips final-HAST sanitization and preserves legacy plugin output. The URL policy still runs, and raw HTML in markdown remains disabled. This mode exists for compatibility with plugins such as Shiki, KaTeX, and Mermaid that generate broad classes, inline styles, SVG, or data URLs. It is not a safe fallback for arbitrary user content. ## Plugin compatibility | Integration | Safe mode | Trusted mode | | ------------------------ | ---------------------------------------------------------------------- | ----------------------- | | CommonMark | Preserved | Preserved | | GitHub Flavored Markdown | Preserved by the default schema | Preserved | | Shiki | Code remains readable; highlighting classes and styles are removed | Full highlighted output | | KaTeX | Math text remains readable; typesetting classes and styles are removed | Full typeset output | | Mermaid | Diagram source remains; rendered/enhanced output is removed | Full plugin output | Plugin code always executes with the host application's privileges. Final-HAST sanitization controls its rendered output; it does not sandbox the plugin itself. ## Custom schemas All renderers accept `sanitizeSchema`. Start from the exported default rather than replacing it accidentally: ```ts import { defaultSanitizeSchema, type SanitizeSchema } from 'vuemarkik'; const schema: SanitizeSchema = { ...defaultSanitizeSchema, attributes: { ...defaultSanitizeSchema.attributes, code: [ ...(defaultSanitizeSchema.attributes?.code ?? []), ['className', 'language-javascript'], ], }, }; ``` ```vue ``` A custom schema changes the security boundary. In particular, do not broadly allow `style`, event properties, scripts, embedded browsing contexts, SVG, or data URLs merely to preserve a plugin's appearance. Prefer a narrowly tested allowlist or trusted mode for content that is genuinely trusted. ## Custom URL policies All rendering components accept `urlTransform`: ```vue ``` The callback receives the URL value, HAST property name, and a read-only TypeScript view of the current HAST element, which has been sanitized in safe mode. Return a string to keep or rewrite the property, or `undefined` to remove it. A custom transform replaces the default URL policy and therefore changes the security boundary. To extend the default safely, call it first: ```ts import { defaultUrlTransform, type UrlTransform } from 'vuemarkik'; const transformUrl: UrlTransform = (url) => { const safeUrl = defaultUrlTransform(url); return safeUrl ? addApplicationTracking(safeUrl) : undefined; }; ``` ## Remaining trust boundary Safe mode does not promise: * Safety of the JavaScript executed by plugins, custom components, or slots. * Safety after a custom schema or URL transform broadens the defaults. * Privacy or safety of remote resources reached through allowed URLs. * Protection from denial of service caused by extremely large or pathological markdown or expensive plugins. Apply application-level input-size and update-rate limits when content is attacker-controlled. Move expensive processing into an appropriately isolated worker when availability is important. ## Upstream security model VueMarkik follows the unified ecosystem's guidance: * [remark security](https://github.com/remarkjs/remark#security) * [remark-rehype security](https://github.com/remarkjs/remark-rehype#security) * [rehype security](https://github.com/rehypejs/rehype#security) * [hast-util-to-jsx-runtime security](https://github.com/syntax-tree/hast-util-to-jsx-runtime#security) * [rehype-sanitize security](https://github.com/rehypejs/rehype-sanitize#security) These projects recommend sanitizing user-controlled HAST after the last unsafe operation. VueMarkik owns that final boundary in safe mode so consumer plugin ordering cannot bypass it. ## Reporting a vulnerability Please use [GitHub private vulnerability reporting](https://github.com/taugr/vuemarkik/security) instead of opening a public issue. --- --- url: /guide/migrating-to-v2.md --- # Migrating to v2 VueMarkik v2 makes final-HAST sanitization the default. Normal CommonMark and GitHub Flavored Markdown continue to render, while plugin-created elements, properties, classes, and styles outside the safe schema are removed. ## Upgrade and run your tests Install VueMarkik v2: ```sh pnpm add vuemarkik@^2 ``` Exercise every markdown source, plugin, custom component, slot, browser-rendered surface, and SSR surface in your application. Pay particular attention to syntax highlighting, math, diagrams, custom classes, inline styles, SVG, and embedded content. ## Choose a migration path ### Keep safe mode No prop is required: ```vue ``` Prefer this path for user content, LLM output, comments, documentation input, and any markdown crossing a trust boundary. If a plugin loses nonessential presentation, keep safe mode and style the remaining semantic markup yourself. If specific properties are essential, extend `defaultSanitizeSchema` with the smallest tested allowlist. ### Temporarily use trusted mode To preserve VueMarkik 1.x plugin output: ```vue ``` Only use this with content and plugins you trust. Record each trusted-mode use as a security exception and test a path back to safe mode. Current compatibility: * GFM works with the default safe schema. * Shiki highlighting requires trusted mode unless you maintain a narrow custom schema for its generated output. * KaTeX typesetting requires trusted mode unless you maintain a narrow custom schema. * Mermaid rendering requires trusted mode; its SVG and data-URL strategies are intentionally outside the default schema. ## Review custom URL transforms The default URL policy allows relative URLs and the `http`, `https`, `mailto`, and `tel` protocols. A custom `urlTransform` replaces that policy. Call `defaultUrlTransform` inside your custom callback unless you deliberately own a different security policy. ## Review custom components and slots In safe mode, custom components and slots receive sanitized HAST nodes and element properties. Their own Vue code remains trusted and can still create unsafe behavior, so review code that forwards URLs, creates HTML, loads remote resources, or executes content. ## Verify before removing trusted mode Before declaring an application migrated: 1. Test representative and adversarial markdown in the browser and under SSR. 2. Verify unsafe elements, event properties, styles, and protocols are absent. 3. Confirm documented plugin output remains usable. 4. Confirm custom components receive only the expected sanitized properties. 5. Remove temporary trusted-mode exceptions where possible. See [Security](./security) for the full threat model and schema guidance. --- --- url: /guide/remark-rehype-plugins.md --- # Plugins VueMarkik utilizes *remark* and *rehype* to parse and process markdown to Vue components. Both of these projects have a rich ecosystem of plugins, enabling support of a variety of markdown formats and extensions, and the ability to transform the rendered output in any desired way. ::: warning Security boundary Plugins execute application code and can add arbitrary HAST elements and properties. Safe mode sanitizes their final output, but the plugin code itself still runs with your application's privileges. Only use plugins you trust and review the [security guide](./security) when the markdown source is untrusted. If a plugin's presentation is removed by the default schema, audit it before using `security-mode="trusted"` or a custom schema. ::: ## Remark Plugins Remark plugins can inspect and transform Markdown. Their most common use is to support extensions to the CommonMark syntax, such as GitHub Flavored Markdown (GFM) and MDX. The remark repo maintains [lists of available plugins](https://github.com/remarkjs/remark?tab=readme-ov-file#plugins). ### Examples * [GitHub Flavored Markdown](./github-flavored-markdown.md) ## Rehype Plugins Rehype plugins change the HTML that is rendered from markdown. Common scenarios include code syntax highlighting, diagram rendering, and math typesetting. The rehype repo maintains [lists of available plugins](https://github.com/rehypejs/rehype?tab=readme-ov-file#plugins). ### Examples * [Syntax Highlighting](./syntax-highlighting.md) * [Mermaid Diagrams](./mermaid.md) * [KaTeX Math](./katex.md) --- --- url: /guide/custom-vue-components.md --- # Custom Vue Components VueMarkik allows you to customize how markdown elements are rendered by providing your own Vue components. This gives you complete control over the styling and behavior of any HTML element in your rendered markdown. There are two approaches to customize markdown rendering: 1. **Using the `components` prop** - Pass custom Vue components defined in separate files 2. **Using Vue template slots** - Define custom rendering inline with template slots Both approaches give you full control over styling and behavior. Choose based on whether you want to reuse components across multiple places (use `components` prop) or keep customization inline (use slots). ::: tip Precedence If you define both a custom component (via `components` prop) and a slot for the same HTML element, **the slot will take precedence**. This allows you to override component-level customizations on a per-instance basis. ::: ## Using Custom Vue Components The `components` prop accepts an object mapping HTML element names to Vue components. This is ideal when you want to reuse the same custom styling across multiple markdown instances. ### Example: Custom H1 Component First, create a Vue component for your custom element: ```vue ``` ::: tip The `` element is required to render the child nodes (text content, nested elements, etc.) of the markdown element. Without it, your custom component would be empty. ::: Then use it by passing it to the `components` prop: ```vue ``` **Live Demo:** ::: raw ::: ### Example: Custom Code Component You can customize any HTML element, including inline code: ```vue ``` Use it the same way: ```vue ``` **Live Demo:** ::: raw ::: ## Using Vue Template Slots Instead of creating separate component files, you can use Vue template slots to customize rendering inline. This approach uses the `MarkdownChildNodes` component to render the element's children. ### Example: Custom H1 with Slots ```vue ``` Slot props always include `childMarkdown`, which contains the rendered children. `MarkdownChildNodes` renders those children for you, and other element props such as `href` are passed through alongside it. **Live Demo:** ::: raw \