Using wafermap in Vue 3¶
This guide is for developers integrating wafermap into a TypeScript + Vue 3 application.
Install¶
What each side owns¶
Your Vue app owns: fetching data, reactive state, component lifecycle, mount and cleanup.
wafermap owns: wafer geometry, die generation, data mapping, and canvas rendering.
Recommended flow¶
- Load your test data — rows with
x,y(die grid positions),hbin,sbin,testValues - Call
buildWaferMap()outside or inonMounted— it is pure and has no DOM dependency - Call
renderWaferMap()insideonMounted— it is DOM-only and must never run during SSR - Always call
ctrl.destroy()inonUnmountedto release canvas resources
x and y in your data are die grid positions (prober step coordinates — integers such as −7, 0, 5), not millimetre values. Pass dieConfig: { width, height } in mm to supply physical die dimensions; omit it and the library estimates them from the grid layout.
Key rules¶
renderWaferMapis DOM-only — call it insideonMounted, never in the<script setup>top level, which runs during SSR in Nuxt- It returns a controller; always call
.destroy()inonUnmountedto prevent resource leaks buildWaferMapis pure and safe to call anywhere — a Pinia store action, a composable, or a route loader
Minimal single-map component¶
renderWaferMap creates and manages its own <canvas> — pass a plain <div> sized to the desired display area:
<script setup lang="ts">
import { ref, watch, onMounted, onUnmounted } from 'vue';
import { buildWaferMap, type DieResult } from '@paulrobins/wafermap';
import { renderWaferMap, type WaferMapController } from '@paulrobins/wafermap/render';
const props = defineProps<{ rows: DieResult[] }>();
const containerEl = ref<HTMLDivElement | null>(null);
let ctrl: WaferMapController | null = null;
function mount() {
if (!containerEl.value) return;
ctrl?.destroy();
const result = buildWaferMap({ results: props.rows });
ctrl = renderWaferMap(containerEl.value, result, {
viewOptions: { plotMode: 'hardBin' },
});
}
onMounted(mount);
onUnmounted(() => ctrl?.destroy());
// Full remount when the data changes.
watch(() => props.rows, mount);
</script>
<template>
<div ref="containerEl" style="width: 100%; aspect-ratio: 1" />
</template>
Reacting to options changes without remounting¶
When only display options change (plot mode, colour scheme, rotation), call ctrl.setOptions() instead of remounting:
<script setup lang="ts">
import { ref, watch, onMounted, onUnmounted } from 'vue';
import { buildWaferMap, type DieResult } from '@paulrobins/wafermap';
import { renderWaferMap, type WaferMapController, type WaferViewOptions } from '@paulrobins/wafermap/render';
const props = defineProps<{
rows: DieResult[];
plotMode?: WaferViewOptions['plotMode'];
}>();
const containerEl = ref<HTMLDivElement | null>(null);
let ctrl: WaferMapController | null = null;
onMounted(() => {
if (!containerEl.value) return;
const result = buildWaferMap({ results: props.rows });
ctrl = renderWaferMap(containerEl.value, result, {
viewOptions: { plotMode: props.plotMode ?? 'hardBin' },
});
});
onUnmounted(() => ctrl?.destroy());
// Data changed — full remount.
watch(() => props.rows, () => {
if (!containerEl.value) return;
ctrl?.destroy();
const result = buildWaferMap({ results: props.rows });
ctrl = renderWaferMap(containerEl.value, result, {
viewOptions: { plotMode: props.plotMode ?? 'hardBin' },
});
});
// Options-only change — no remount needed.
watch(() => props.plotMode, (mode) => {
ctrl?.setOptions({ plotMode: mode ?? 'hardBin' });
});
</script>
<template>
<div ref="containerEl" style="width: 100%; aspect-ratio: 1" />
</template>
Gallery component¶
Pass an array of WaferMapDisplayItem objects — each is a WaferMapResult with an optional label and per-card callbacks spread in. Factory functions keep the page responsive by building each card in a deferred task:
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import { buildWaferMap, type DieResult } from '@paulrobins/wafermap';
import { renderWaferGallery, type GalleryController, type WaferMapDisplayItemFactory } from '@paulrobins/wafermap/render';
const props = defineProps<{
wafers: Array<{ label: string; rows: DieResult[] }>;
}>();
const containerEl = ref<HTMLDivElement | null>(null);
let ctrl: GalleryController | null = null;
onMounted(() => {
if (!containerEl.value) return;
// Factory functions keep the page responsive — each card is built in a
// deferred task so the gallery shell appears immediately.
const factories: WaferMapDisplayItemFactory[] = props.wafers.map((w) => () => ({
...buildWaferMap({ results: w.rows }),
label: w.label,
}));
ctrl = renderWaferGallery(containerEl.value, factories);
});
onUnmounted(() => ctrl?.destroy());
</script>
<template>
<div ref="containerEl" />
</template>
Call ctrl.setItems(newFactories) to replace the lot after mount, or ctrl.setOptions({ plotMode: 'value' }) to change display options across all cards without rebuilding.
Adding statistical findings¶
analyzeWaferMap is a pure function — run it alongside buildWaferMap and pass the result to renderWaferMap. A Findings button appears in the toolbar automatically.
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue';
import { buildWaferMap, type DieResult } from '@paulrobins/wafermap';
import { analyzeWaferMap } from '@paulrobins/wafermap/stats';
import { renderWaferMap, type WaferMapController } from '@paulrobins/wafermap/render';
const props = defineProps<{ rows: DieResult[] }>();
const containerEl = ref<HTMLDivElement | null>(null);
let ctrl: WaferMapController | null = null;
onMounted(() => {
if (!containerEl.value) return;
const result = buildWaferMap({ results: props.rows });
const summary = analyzeWaferMap(result);
ctrl = renderWaferMap(containerEl.value, result, { statsSummary: summary });
});
onUnmounted(() => ctrl?.destroy());
</script>
<template>
<div ref="containerEl" style="width: 100%; aspect-ratio: 1" />
</template>
To show a persistent summary panel instead of the toolbar button, add a summaryPanel option:
ctrl = renderWaferMap(containerEl.value, result, {
statsSummary: summary,
summaryPanel: { placement: 'right' },
});
Running buildWaferMap outside the component¶
buildWaferMap has no DOM dependency — extract it to a Pinia store action, a composable, or a route loader and pass the result in as a prop:
// stores/wafer.ts
import { defineStore } from 'pinia';
import { buildWaferMap, type WaferMapResult } from '@paulrobins/wafermap';
export const useWaferStore = defineStore('wafer', {
state: () => ({ result: null as WaferMapResult | null }),
actions: {
async load(rows) {
this.result = buildWaferMap({ results: rows });
},
},
});
Notes for Nuxt¶
- Wrap the component in
<ClientOnly>or guard withif (import.meta.client)—renderWaferMaprequires the DOM and will throw during server render buildWaferMapis safe to call in a Nuxt server route oruseFetch— only the renderer calls need to be deferred to mount