# How to Use the SearchDialog Component for Global Search in Celeris Web

> Implement global search in Celeris Web using the SearchDialog component. Aggregate router menu items and custom groups for a seamless user experience. Learn how to trigger search anywhere.

- Repository: [Kirk Lin/celeris-web](https://github.com/kirklin/celeris-web)
- Tags: how-to-guide
- Published: 2026-03-05

---

**The SearchDialog component provides a reusable modal interface that aggregates searchable items from the Vue Router menu and custom groups, enabling global search functionality that can be triggered from anywhere in the Celeris Web admin application.**

The **SearchDialog** component in the `kirklin/celeris-web` repository delivers a unified "search-anywhere" experience for Vue.js admin interfaces. By combining the reactive state management of the **`useSearchDialog`** composable with automatically generated search groups derived from the application router, developers can implement a centralized global search system that filters navigable pages and executes custom actions when users select results.

## Understanding the SearchDialog Architecture

### Core Component Structure

The [`SearchDialog.vue`](https://github.com/kirklin/celeris-web/blob/main/SearchDialog.vue) component located at [`apps/admin/src/component/SearchDialog/src/SearchDialog.vue`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/component/SearchDialog/src/SearchDialog.vue) serves as the visual container for the global search experience. It renders a modal overlay containing a search input field, grouped result lists, and keyboard navigation handlers. Internally, the component constructs its default dataset from **`shallowMenus`**, which represents the flattened navigation tree of the Vue Router configuration, ensuring every registered admin route appears in the search index without manual configuration.

### The useSearchDialog Composable

Located at [`apps/admin/src/composables/useSearchDialog.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/composables/useSearchDialog.ts), this composable exports a reactive state object containing:

- **`open`**: A boolean ref controlling the dialog's visibility state
- **`trigger(open)`**: A method to programmatically display the dialog by passing the open state
- **`close`**: A method to dismiss the dialog and reset focus
- **`searchGroups`**: A reactive array of `SearchResultItem` groups that powers the filterable result list

Each item in `searchGroups` must conform to the interface defined in [`apps/admin/src/component/SearchDialog/src/types.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/component/SearchDialog/src/types.ts), requiring a unique `key`, display `title`, filterable `label`, and an **`action`** callback function that executes when the user selects the item.

## Triggering the Search Dialog

### Header Button Integration

The [`SearchAnyWhere.vue`](https://github.com/kirklin/celeris-web/blob/main/SearchAnyWhere.vue) component at [`apps/admin/src/layouts/header/components/SearchAnyWhere.vue`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/layouts/header/components/SearchAnyWhere.vue) demonstrates the standard implementation pattern. Import the composable, destructure the `trigger` method and `open` ref, then invoke `trigger(open)` in response to user interactions.

```vue
<script setup lang="ts">
import { useSearchDialog } from '~/composables/useSearchDialog'

const { trigger, open } = useSearchDialog()

function openSearch() {
  trigger(open)
}
</script>

<template>
  <button @click="openSearch" class="header-search-btn">
    <CAIcon name="tabler:search" size="16" />
    Search…
  </button>
  
  <!-- Globally registered or locally imported -->
  <SearchDialog />
</template>

```

### Keyboard Shortcut Integration

Bind global keyboard shortcuts to the `trigger(open)` method for power-user accessibility. The component supports standard patterns like **Cmd+K** (macOS) or **Ctrl+K** (Windows/Linux) to immediately surface the global search interface from any application state.

```typescript
import { onMounted } from 'vue'
import { useSearchDialog } from '~/composables/useSearchDialog'

export default {
  setup() {
    const { trigger, open } = useSearchDialog()

    onMounted(() => {
      window.addEventListener('keydown', (e) => {
        if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
          e.preventDefault()
          trigger(open)
        }
      })
    })
  },
}

```

## Extending Global Search with Custom Groups

Beyond the router-derived navigation items, extend the `searchGroups` ref to include external documentation, API endpoints, or application-specific commands. Push new group objects containing a `name` and an `items` array, where each item specifies its execution logic through the `action` property.

```typescript
import { useSearchDialog } from '~/composables/useSearchDialog'

const { searchGroups } = useSearchDialog()

const documentationGroup = {
  name: 'Documentation',
  items: [
    {
      iconName: 'tabler:book',
      iconImage: null,
      key: 'docs-getting-started',
      title: 'Getting Started Guide',
      label: 'Docs – Getting Started',
      action: () => window.open('https://example.com/docs', '_blank')
    },
    {
      iconName: 'tabler:api',
      iconImage: null,
      key: 'docs-api',
      title: 'API Reference',
      label: 'Docs – API Reference',
      action: () => window.open('https://example.com/api', '_blank')
    }
  ]
}

// Add to the reactive search index
searchGroups.value.push(documentationGroup)

```

## Direct Component Usage

For scenarios requiring local state isolation rather than the global composable state, import `SearchDialog` directly and bind the `v-model:open` prop. This approach bypasses the `useSearchDialog` store in favor of component-level reactivity.

```vue
<template>
  <div>
    <button @click="isOpen = true">Open Search</button>
    <SearchDialog v-model:open="isOpen" />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import SearchDialog from '~/component/SearchDialog/src/SearchDialog.vue'

const isOpen = ref(false)
</script>

```

## Summary

- The **SearchDialog** component at [`apps/admin/src/component/SearchDialog/src/SearchDialog.vue`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/component/SearchDialog/src/SearchDialog.vue) renders the modal interface and automatically indexes routes from **`shallowMenus`**.
- The **`useSearchDialog`** composable exposes `open`, `trigger(open)`, `close`, and the reactive **`searchGroups`** array for state management.
- Search items require `key`, `title`, `label`, and an **`action`** callback that executes upon selection, usually performing `router.push` or `window.open`.
- Invoke the dialog via `trigger(open)` or by setting `open.value = true`, enabling integration with keyboard shortcuts, header buttons, or programmatic events.
- Custom search groups can be added to `searchGroups.value` to extend global search beyond the application router to external resources and custom commands.

## Frequently Asked Questions

### How do I open the SearchDialog programmatically without a button click?

Import the `useSearchDialog` composable from `~/composables/useSearchDialog` and call `trigger(open)` or set `open.value = true` directly inside any function, lifecycle hook, or API response handler. This pattern enables opening the global search from keyboard events, websocket messages, or automated workflows anywhere in the application.

### Can I search content that is not part of the Vue Router configuration?

Yes. While the component automatically generates search items from the router's `shallowMenus`, you can extend the `searchGroups` ref returned by `useSearchDialog` with custom groups containing items for external links, documentation pages, or specific application actions. Each custom item must provide a unique `key`, display text, and an `action` callback.

### What happens when a user selects a search result?

The [`SearchDialog.vue`](https://github.com/kirklin/celeris-web/blob/main/SearchDialog.vue) component executes the selected item's **`action`** callback function and immediately sets the `open` state to `false`, closing the modal. For navigation items, this typically invokes `router.push` to navigate to the target page, while custom items might open external URLs or trigger application-specific functions.

### Where are the TypeScript interfaces for search items defined?

The type definitions for `SearchResultItem` and related interfaces reside in [`apps/admin/src/component/SearchDialog/src/types.ts`](https://github.com/kirklin/celeris-web/blob/main/apps/admin/src/component/SearchDialog/src/types.ts). These interfaces specify the required properties including `key: string`, `title: string`, `label: string`, optional `iconName` and `iconImage`, and the mandatory `action: () => void` function signature.