Skip to content

Worker Kit

v0.2.5DX & ReliabilityVue

Type-safe Web Worker composables for Vue 3 — useWorker(), a worker pool, and a reactive useWorkerComputed(), with types inferred from the worker file itself.

Worker Kit
Get started →
npm install vue-worker-kit@latest
01 — Purpose

When you'd reach for this

A UI that stutters from heavy synchronous computations is a clear sign it's time to move that work off the main thread — vue-worker-kit gives you Vue composables for exactly that, built on the real Web Worker API.

A 100k-row table freezes on every sort

Client-side sorting, filtering, or parsing of a large dataset runs in a real system thread — the UI stays responsive even during a heavy recalculation.

Live search recalculates on every keystroke

The stale run cancels itself, and a new one starts in the background thread a moment later — you can type faster than the worker computes, and the UI never stutters.

Bulk file or image processing in the browser

A worker pool spreads resizing, format conversion, or thumbnail generation for hundreds of files across multiple CPU cores, and large binary data moves zero-copy via transferables.

The same dashboard is open in several tabs

A SharedWorker computes the heavy state once for every tab instead of each tab recalculating it separately — switching tabs feels instant.

02 — Features

At a glance

Offload heavy computations to a separate thread without blocking the UI

Offload heavy computations to a separate thread without blocking the UI

useWorker runs heavy synchronous tasks (sorting, parsing, image processing) in a real system thread (Worker). The main thread stays free for rendering and event handling — the UI never freezes, even during complex calculations that would cause noticeable jank on the main thread.

Worker pool for parallel data processing

Worker pool for parallel data processing

createWorkerPool creates a pool of workers, distributing tasks among them with bounded concurrency. The pool.map method processes arrays of data concurrently while preserving result order — this provides real speedups on multi‑core systems by executing in parallel across multiple CPU cores simultaneously, especially effective for batch image processing or calculations.

Reactive background computations via useWorkerComputed

Reactive background computations via useWorkerComputed

useWorkerComputed works like a reactive computed, but all heavy recalculations run inside a worker. When dependencies change (e.g., as you type), any outdated run is automatically cancelled and a new one starts with debouncing. The result is always up‑to‑date, while the main thread stays responsive even under frequent source changes.

End‑to‑end typing without duplication or manual annotations

End‑to‑end typing without duplication or manual annotations

Input and output types are inferred automatically from the worker file itself via typeof import. You don’t need to describe them twice — neither in the worker nor on the calling side. This provides full type safety, autocompletion, and compile‑time checking without extra boilerplate between the main thread and the worker.

Advanced control: transferables, streaming, cache, and retries

Advanced control: transferables, streaming, cache, and retries

Pass large binary data (ArrayBuffer, OffscreenCanvas) zero‑copy via transferables. Receive intermediate results via streaming (reportChunk) for progressive rendering. Enable LRU caching for repeated computations and automatic retries with exponential backoff for fault‑tolerant operations. A built‑in DevTools panel shows pool state and tasks.

Cross-tab shared workers and warmup

Cross-tab shared workers and warmup

useSharedWorker connects to a single SharedWorker shared by every open tab of the site — heavy state gets computed once instead of separately in each tab. warmup() starts the worker ahead of time, eliminating cold‑start latency. Idle workers shut themselves down on a timeout, and onScopeDispose cleans up on unmount.

03 — Quick example

See how it works

The worker handler

The heavy lifting — sorting a large array — runs on a separate thread instead of blocking the UI, reports progress, and can be aborted via a cancel signal.

heavy-sort.worker.ts
import { defineWorkerHandler } from 'vue-worker-kit/worker'

export default defineWorkerHandler(async (data: number[], ctx) => {
  for (let i = 0; i < data.length; i++) {
    if (ctx.signal.aborted) throw ctx.signal.reason
    if (i % 10_000 === 0) ctx.reportProgress(i / data.length)
  }
  return data.sort((a, b) => a - b)
})

Running it from a component

useWorker() creates the worker lazily and infers the result type straight from the handler — no generic annotation needed.

component.ts
import { useWorker } from 'vue-worker-kit'

const { run, isRunning, progress, error, cancel } = useWorker<typeof import('./heavy-sort.worker')>(
  () => new Worker(new URL('./heavy-sort.worker.ts', import.meta.url), { type: 'module' }),
)

const sorted = await run(hugeArray, { transfer: [hugeArray.buffer] })
// sorted: number[] — inferred from heavy-sort.worker.ts, no generic annotation needed

Intermediate results, not just the final one

With streaming: true the worker reports chunks as it goes via ctx.reportChunk() — chunks updates reactively while the final result is still pending.

streaming.ts
import { useWorker } from 'vue-worker-kit'

const { run, chunks, isRunning } = useWorker<typeof import('./process.worker')>(
  () => new Worker(new URL('./process.worker.ts', import.meta.url), { type: 'module' }),
  { streaming: true }, // required — without it `chunks` is undefined, not a ref
)

const finalResult = await run(largeDataset)

watch(chunks, (newChunks) => {
  console.log('Received chunk:', newChunks.at(-1))
})

// Worker-side calls ctx.reportChunk(data) as it processes each batch —
// chunks.value fills in progressively while run() is still pending.