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

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 component located at 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, 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, 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 component at 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.

<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.

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.

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.

<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 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 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. These interfaces specify the required properties including key: string, title: string, label: string, optional iconName and iconImage, and the mandatory action: () => void function signature.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →