Heavy sorting without blocking the UI: Worker Kit + Virtual Scroller Kit
A list of 100,000+ rows isn't a problem by itself — Virtual Scroller Kit only renders the visible rows. The problem starts once that list is also sorted or filtered on every change: Array.prototype.sort on an array that size is synchronous, main-thread-blocking work, and async/await doesn't save you here — the sort loop itself still runs on the same thread that's drawing the UI.
Worker Kit moves the sort itself onto a real Worker thread, and VirtualList renders the already-sorted result.
The worker
// sort-rows.worker.ts
import { defineWorkerHandler } from 'vue-worker-kit/worker'
interface Row {
id: number
text: string
score: number
}
export default defineWorkerHandler(async (data: Row[], ctx) => {
return data.sort((a, b) => b.score - a.score)
})The component
<script setup lang="ts">
import { ref } from 'vue'
import { useWorker } from 'vue-worker-kit'
import { VirtualList } from 'vue-virtual-scroller-kit'
interface Row {
id: number
text: string
score: number
}
const { run, isRunning } = useWorker<typeof import('./sort-rows.worker')>(
() => new Worker(new URL('./sort-rows.worker.ts', import.meta.url), { type: 'module' }),
)
const rows = ref<Row[]>([])
async function resort(source: Row[]) {
// sorted: Row[] — inferred from sort-rows.worker.ts, no generic needed
rows.value = await run(source)
}
</script>
<template>
<p v-if="isRunning">Sorting…</p>
<VirtualList :items="rows" :estimated-item-size="48" style="height: 600px">
<template #default="{ item }">
<div style="padding: 12px 16px; border-bottom: 1px solid #eee">{{ item.text }}</div>
</template>
</VirtualList>
</template>Why this pair, and not either one alone
VirtualList on its own doesn't speed up the sort — it only speeds up rendering. Without the worker, the UI still freezes for the duration of sort(); the list is just long and fast afterward. The worker on its own isn't necessary for small lists either — postMessage and structural cloning have a real cost, and for a 500-row list, sorting on the main thread finishes faster. The pair earns its keep at the scale where the problem is real: tens or hundreds of thousands of rows, re-sorted often (clicking a column header, typing into a search box).
What's next
- For truly large datasets processed in batches,
createWorkerPool()instead of a singleuseWorker(). See Worker Kit — overview. - For a table view instead of a plain row list,
VirtualTableinstead ofVirtualList, same idea. See Virtual Scroller Kit — overview.