Guide

Marks as Children

Define Observable Plot marks as Vue template components with the Plot prefix, stack multiple marks, and understand the children-over-props rule.

Use <Plot*> components directly in your Vue template. Each mark from Observable Plot maps to a component with Plot prefix and PascalCase naming.

Basic example

App.vue
<script setup lang="ts">
import { VPlot } from '@memotux/vue-plot'

const data = [
  { name: 'A', value: 10 },
  { name: 'B', value: 20 },
  { name: 'C', value: 15 },
]
</script>

<template>
  <VPlot :width="680">
    <PlotBarY :data="data" x="name" y="value" />
  </VPlot>
</template>

Multiple marks

Stack multiple marks in a single plot:

App.vue
<template>
  <VPlot :width="680" :height="400">
    <PlotBarY :data="data" x="name" y="value" />
    <PlotRuleY :data="[0]" stroke="gray" />
    <PlotText
      :data="data"
      x="name"
      y="value"
      dy="-8"
      textAnchor="middle"
    />
  </VPlot>
</template>

Mark naming convention

The mapping from Observable Plot functions to Vue components follows a consistent pattern:

Observable Plot functionVue Component
barY()<PlotBarY>
dot()<PlotDot>
lineX()<PlotLineX>
areaY()<PlotAreaY>
ruleX()<PlotRuleX>

The rule: Plot + capitalize the mark name.

Passing data

Each mark component accepts a data prop plus all options from its corresponding Observable Plot function:

App.vue
<PlotDot
  :data="scatterData"
  x="weight"
  y="height"
  r="age"
  fill="blue"
  tip
/>

The data prop is separate from the mark options — it's not mixed in with channels like x, y, fill.

Combining mark types

Mix different mark types to build complex visualizations:

App.vue
<template>
  <VPlot :width="680" :height="400">
    <!-- Background frame -->
    <PlotFrame stroke="#eee" />

    <!-- Data points -->
    <PlotDot :data="points" x="x" y="y" fill="steelblue" />

    <!-- Trend line -->
    <PlotLinearRegressionY
      :data="points"
      x="x"
      y="y"
      stroke="red"
      strokeWidth="2"
    />

    <!-- Reference line -->
    <PlotRuleY :data="[0]" stroke="#ccc" />
  </VPlot>
</template>

Priority rule

If marks are provided both as props and as children, only child marks are rendered:

App.vue
<!-- Only PlotBarY renders — the marks prop is ignored -->
<VPlot :marks="[frame()]">
  <PlotBarY :data="data" x="name" y="value" />
</VPlot>

Requirements

Using mark components as children requires the Vite plugin configuration:

vite.config.ts

import { plotCustomElement } from '@memotux/vue-plot'

export default defineConfig({
  plugins: [
    vue({
      template: plotCustomElement.template,
    }),
  ],
})
When using camelCase attributes like textAnchor or strokeWidth inside a Single-File Component, Vue's compiler maps them correctly. If you instead place these marks directly in a DOM template (e.g. an index.html<div id="app">), the browser lowercases attribute names and the props won't bind. Prefer SFC usage, or use kebab-case equivalents such as text-anchor and stroke-width in DOM templates.

What's next?

Copyright © 2026