Skip to content
vue-cmdk
Esc
↑↓navigate↵open⌘Jpreview
On this page

Recents

Pin the commands people use most, and store them wherever you like.

Recents are on by default and live in localStorage. When the query is empty, commands someone has picked before show up first in a “Recent” group.

They’re ranked by a score that mixes how often and how recently a command was used: its use count, plus a bonus that fades to nothing over thirty days. The palette keeps 30 entries and shows 5.

Options

<CommandPaletteDialog :commands="commands" :recents="{ namespace: user.id, display: 5 }" />
<CommandPaletteDialog :commands="commands" :recents="false" />
option default meaning
key vue-cmdk:recents The localStorage key for the default adapter.
namespace none Keeps separate lists under one key, for example one per account.
max 30 Entries kept in storage.
display 5 Entries shown in the Recent group.
storage localStorage Your own adapter. See below.

Two palettes on one page should use different keys or namespaces, or they will share a list.

What gets recorded

Every action and link is recorded when it runs, unless it sets trackRecent: false. Opening a page isn’t recorded. Ids that no longer match a command are skipped when the list is drawn, so a removed command never leaves a dead row behind.

Removing recents

While a row in the Recent group is selected, Shift+Delete removes it from recents. Apple keyboards have no Delete key, so Shift+Backspace does the same there. The selection stays where it was, the footer shows the key while a recent row is selected, and screen readers hear “Removed … from recent”.

To remove entries from code, for example from a “Clear history” button on a settings page, call the methods on a template ref:

<script setup lang="ts">
import { useTemplateRef } from 'vue'

const palette = useTemplateRef('palette')
</script>

<template>
  <CommandPaletteDialog ref="palette" :commands="commands" />
  <button type="button" @click="palette?.clearRecents()">Clear history</button>
</template>

forgetRecent(id) removes one command, and clearRecents() removes every entry of the current namespace. Other namespaces are kept. Both save through the configured storage, and both work while the dialog is closed. The headless composable has the same two methods.

Your own storage

To keep recents on a server or in IndexedDB, pass an adapter. Both methods can be sync or async:

import type { RecentsAdapter } from 'vue-cmdk-palette'

const storage: RecentsAdapter = {
  load: () => api.get('/recents'),
  save: (entries) => api.put('/recents', entries),
}

Whatever load returns is checked, and malformed entries are dropped. If someone picks, removes or clears before an async load has finished, the change is kept and applied to the stored list once it arrives.

The default adapter swallows storage errors, such as a private window with storage blocked, so recents stay empty.

Was this page helpful?