Vue components for building data visualizations with @observablehq/plot.
Note
This project is a work in progress. APIs may change between minor versions.
pnpm add @memotux/vue-plot @observablehq/plot// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { plotCustomElement } from '@memotux/vue-plot'
export default defineConfig({
plugins: [
vue({
template: plotCustomElement.template,
}),
],
})<!-- App.vue -->
<script setup>
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>pnpm add @memotux/vue-plot @observablehq/plot
# or
npm install @memotux/vue-plot @observablehq/plot
# or
yarn add @memotux/vue-plot @observablehq/plotImportant
@observablehq/plot is a required peer dependency. It is NOT bundled with this package — you must install it separately.
Configure the Vite plugin so Vue recognizes <Plot*> tags as custom elements:
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { plotCustomElement } from '@memotux/vue-plot'
export default defineConfig({
plugins: [
vue({
template: plotCustomElement.template,
}),
],
})Note
This step is only required when using mark components as children. If you prefer to pass marks via the marks prop, you can skip this configuration.
Choose one of the following methods to use <VPlot> in your components.
Registers <VPlot> globally across your application.
// main.ts
import { createApp } from 'vue'
import { VuePlot } from '@memotux/vue-plot'
import App from './App.vue'
const app = createApp(App)
app.use(VuePlot)
app.mount('#app')Import <VPlot> directly in individual components without global registration.
<!-- MyChart.vue -->
<script setup>
import { VPlot } from '@memotux/vue-plot'
</script>
<template>
<VPlot :width="680" :marks="[]" />
</template>Use <Plot*> components directly in your template. Each mark from @observablehq/plot maps to a Plot{MarkName} component with PascalCase naming.
<script setup>
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>Note
This pattern requires the Vite Plugin Configuration setup step.
Pass marks from @observablehq/plot through the marks prop. This pattern does not require the Vite plugin.
<script setup>
import { VPlot } from '@memotux/vue-plot'
import { barY } from '@observablehq/plot'
const data = [
{ name: 'A', value: 10 },
{ name: 'B', value: 20 },
{ name: 'C', value: 15 },
]
const options = {
width: 680,
marks: [barY(data, { x: 'name', y: 'value' })],
}
</script>
<template>
<VPlot v-bind="options" />
</template>Bind reactive data to update plots when data changes.
<script setup>
import { ref } from 'vue'
import { VPlot } from '@memotux/vue-plot'
const data = ref([
{ x: 1, y: 2 },
{ x: 2, y: 4 },
{ x: 3, y: 1 },
])
function addPoint() {
data.value.push({
x: data.value.length + 1,
y: Math.random() * 10,
})
}
</script>
<template>
<VPlot :width="680">
<PlotDot :data="data" x="x" y="y" />
</VPlot>
<button @click="addPoint">Add Point</button>
</template>If marks are provided both as props and as children, only child marks are rendered.
<!-- Only PlotBarY renders — the marks prop is ignored -->
<VPlot :marks="[frame()]">
<PlotBarY :data="data" x="name" y="value" />
</VPlot>The main chart component. Accepts plot configuration via props.
| Prop | Type | Description |
|---|---|---|
All PlotOptions fields |
— | Spread any PlotOptions property (width, height, style, etc.) |
marks |
RenderableMark[] |
Array of mark functions from @observablehq/plot |
Usage pattern: Spread a plot options object with v-bind:
<VPlot v-bind="{ width: 680, marks: [...] }" />Every mark from @observablehq/plot is available as a <Plot*> child component.
| Mark Function | Component Tag | Options Type |
|---|---|---|
area |
<PlotArea> |
AreaOptions |
areaX |
<PlotAreaX> |
AreaXOptions |
areaY |
<PlotAreaY> |
AreaYOptions |
barX |
<PlotBarX> |
BarXOptions |
barY |
<PlotBarY> |
BarYOptions |
dot |
<PlotDot> |
DotOptions |
dotX |
<PlotDotX> |
DotXOptions |
dotY |
<PlotDotY> |
DotYOptions |
frame |
<PlotFrame> |
FrameOptions |
line |
<PlotLine> |
LineOptions |
lineX |
<PlotLineX> |
LineXOptions |
lineY |
<PlotLineY> |
LineYOptions |
rect |
<PlotRect> |
RectOptions |
rectX |
<PlotRectX> |
RectXOptions |
rectY |
<PlotRectY> |
RectYOptions |
ruleX |
<PlotRuleX> |
RuleXOptions |
ruleY |
<PlotRuleY> |
RuleYOptions |
text |
<PlotText> |
TextOptions |
textX |
<PlotTextX> |
TextXOptions |
textY |
<PlotTextY> |
TextYOptions |
tickX |
<PlotTickX> |
TickXOptions |
tickY |
<PlotTickY> |
TickYOptions |
cell |
<PlotCell> |
CellOptions |
image |
<PlotImage> |
ImageOptions |
arrow |
<PlotArrow> |
ArrowOptions |
link |
<PlotLink> |
LinkOptions |
vector |
<PlotVector> |
VectorOptions |
tree |
<PlotTree> |
TreeOptions |
contour |
<PlotContour> |
ContourOptions |
hexbin |
<PlotHexbin> |
HexbinOptions |
density |
<PlotDensity> |
DensityOptions |
tip |
<PlotTip> |
TipOptions |
geo |
<PlotGeo> |
GeoOptions |
raster |
<PlotRaster> |
RasterOptions |
axisX |
<PlotAxisX> |
AxisXOptions |
axisY |
<PlotAxisY> |
AxisYOptions |
crosshair |
<PlotCrosshair> |
CrosshairOptions |
delaunayLink |
<PlotDelaunayLink> |
DelaunayOptions |
delaunayMesh |
<PlotDelaunayMesh> |
DelaunayOptions |
hexgrid |
<PlotHexgrid> |
HexgridOptions |
hexagon |
<PlotHexagon> |
Omit<DotOptions, "symbol"> |
waffleX |
<PlotWaffleX> |
WaffleXOptions |
waffleY |
<PlotWaffleY> |
WaffleYOptions |
bollinger |
<PlotBollinger> |
BollingerOptions |
boxX |
<PlotBoxX> |
BoxXOptions |
boxY |
<PlotBoxY> |
BoxYOptions |
linearRegressionX |
<PlotLinearRegressionX> |
LinearRegressionXOptions |
linearRegressionY |
<PlotLinearRegressionY> |
LinearRegressionYOptions |
differenceX |
<PlotDifferenceX> |
DifferenceOptions |
differenceY |
<PlotDifferenceY> |
DifferenceOptions |
auto |
<PlotAuto> |
AutoOptions |
Each mark component accepts a data prop plus all options from the corresponding @observablehq/plot mark function.
Import types from @memotux/vue-plot/types:
import type { PlotProps, PlotMarksProps, Marks, PlotContext, PlotChildrenContext } from '@memotux/vue-plot/types'| Type | Description |
|---|---|
PlotProps |
Union of PlotOptions | PlotMarksProps — accepted by VPlot |
PlotMarksProps<M> |
Generic: { data?: Data } & MarksOptions[M] — props for a single mark component |
Marks |
Union of all supported mark names ('area' | 'barY' | 'dot' | ...) |
PlotContext |
Reactive context shared between root plot and child marks |
PlotChildrenContext<M> |
Per-mark context with mark, options, data, inserted, and id |
PlotMarksProps generic example:
import type { PlotMarksProps } from '@memotux/vue-plot/types'
// Props for a barY mark component
type BarYProps = PlotMarksProps<'barY'>
// → { data?: Data } & BarYOptionsThis library is inspired by:
- Observable Plot — the underlying visualization engine
- TresJS — the Vue custom element pattern for Three.js
Contributions are welcome! Please open an issue or submit a pull request.
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Commit your changes (
git commit -m 'feat: add my feature') - Push to the branch (
git push origin feature/my-feature) - Open a Pull Request