Usage in a Vue project
Usage in a Vue project
This is for:
DeveloperThis article explains how to use the Coveo Atomic library in a Vue.js project.
For a complete example you may want to start from or refer to throughout this article, see this Vue.js Search Page. |
Installation
Installing Atomic in your Vue project is very similar to installing Atomic outside of Vue. However, there are some extra steps if you choose to install Atomic via npm.
CDN
Include the following scripts in the target HTML page:
<script
type="module"
src="https://static.cloud.coveo.com/atomic/v3/atomic.esm.js">
</script>
<link
rel="stylesheet"
href="https://static.cloud.coveo.com/atomic/v3/themes/coveo.css"/>
The main framework script. | |
The default stylesheet for the framework. While this import is optional, if you don’t import it you’ll need to define its variables yourself. |
CDN links are available for the following versions of Atomic releases:
Major: https://static.cloud.coveo.com/atomic/v3/atomic.esm.js
Recommended for development and QA environment purposes. It’s a rather safe way to always have recent features from minor version updates, while not having major breaking changes.
Minor: https://static.cloud.coveo.com/atomic/v3.0/atomic.esm.js
Recommended for production and any customer-facing app. Minor version updates are mostly bug fixes; pointing to a minor version is safe and retrieves patches instantly.
NPM
The library is also available as an npm package.
npm install @coveo/atomic
Use a bundler (Browserify, Webpack, Rollup, etc.) to require('@coveo/atomic')
or import '@coveo/atomic'
.
The resources are exposed through the following entry points:
-
@coveo/atomic
(various types and utilities used by Coveo Atomic, such asinitializeBindings
)Example
You want to create a custom component, so you need to import
initializeBindings
.import {initializeBindings} from '@coveo/atomic' // ...
-
@coveo/atomic/loader
(Coveo Atomic components types, as well as thedefineCustomElements
andsetNonce
utilities) -
@coveo/atomic/themes
(sample Coveo Atomic themes) -
@coveo/atomic/assets
(SVG icons used by Coveo Atomic) -
@coveo/atomic/lang
(localization files used by Coveo Atomic)
Note
If you use TypeScript, note that Atomic Vue doesn’t support the |
Load Static Assets
For performance reasons, when using the npm installation, the generated Atomic JavaScript bundle doesn’t automatically include static assets that are loaded on demand. This impacts language support, as well as the use of included SVG icons.
You must make available external assets distributed with Atomic by including them in the public directory of your app. Without this, you’ll face various issues. For example, labels in the app will appear as temporary placeholders.
The location of the public directory depends on how you build, configure and distribute your app.
import {dirname} from 'node:path'
const themeDirectory = dirname(import.meta.resolve('@coveo/atomic/themes/coveo.css'))
const langDirectory = dirname(import.meta.resolve('@coveo/atomic/lang/en.json'))
const assetDirectory = dirname(import.meta.resolve('@coveo/atomic/assets/all.svg'))
// Copy the directories to your public folder
// ...
Note
Be sure to respect the folder hierarchy, with SVG icons under the |
Update Your Entry File
The Atomic package allows you to customize your components by defining CSS variables in your own stylesheet (see Themes and visual customization).
It also exposes a default theme that you can use as is or build upon.
When using npm, you can import the default theme from @coveo/atomic/dist/atomic/themes/coveo.css
in your entry .js
file as exemplified below.
import '@coveo/atomic/dist/atomic/themes/coveo.css';
import {applyPolyfills, defineCustomElements} from '@coveo/atomic/loader';
import {createApp} from 'vue';
import App from './App.vue';
applyPolyfills().then(() => {
defineCustomElements(window);
});
createApp(App).mount('#app');
Optional default themes. While this import is optional, if you don’t import it you’ll need to define its variables yourself. | |
Bind the custom elements to the window object (see Stencil - Vue). |
Stencil and Vite Issue
In a Vue project where you have installed Atomic via npm, you may see the following warning.
The above dynamic import cannot be analyzed by Vite.
See link:https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#limitations[Rollup limitations^] for supported dynamic import formats. If this is intended to be left as-is, use the /* @vite-ignore */ comment inside the import() call to suppress this warning.
This warning is due to a small issue with Stencil and Vite. It shouldn’t affect your application however, and you can suppress the warning if you prefer.
Configuration
Add the following Vue configuration to handle Atomic components.
// vue.config.js
{
...,
chainWebpack: (config) => {
config.module
.rule('html')
.test(/\.html$/)
.use('html-loader')
.loader('html-loader');
config.module
.rule('vue')
.use('vue-loader')
.tap((options) => ({
...options,
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('atomic-'),
},
}));
}
}
Allows for parsing HTML templates, which will be necessary for atomic components that require the native <template> tag. |
|
For this configuration to work, you’ll need to add the html-loader dev dependency to your project.
|
|
Tells Vue to treat any components starting with atomic- as native ones.
This means Vue won’t try to create Vue components out of them. |
Initialize Your Search Interface
After you render your search interface, initialize it as exemplified below. You can do so in any component, but ideally in the app wrapper.
import {onMounted} from 'vue';
async function initInterface() {
await customElements.whenDefined('atomic-search-interface');
const searchInterface = document.querySelector(
'atomic-search-interface'
) as HTMLAtomicSearchInterfaceElement;
await searchInterface.initialize({
accessToken: '<YOUR-TOKEN>',
organizationId: '<YOUR-ORGANIZATION-ID>',
organizationEndpoints: await searchInterface.getOrganizationEndpoints('<YOUR-ORGANIZATION-ID>'),
});
searchInterface.executeFirstSearch();
}
onMounted(initInterface);
Initialization (see Use Components to Create a Search Interface). | |
Triggers the first search. | |
Initializes the interface after the search interface has rendered. |
Use the <atomic-result-template>
Component
Vue treats the <template>
tag as a container for Vue components and therefore doesn’t render it (see Template Refs).
This poses an obstacle when working with the <atomic-result-template>
component, which expects a native <template>
that it will then use to create each result element (see Defining a result template).
To get around this, create your result templates in HTML files (as in the following example, with the src/templates/result-template.html
file) and then import them as follows.
<!-- .vue component -->
<script setup lang="ts">
import resultTemplate from '../templates/result-template.html';
</script>
<template>
<atomic-result-list>
<atomic-result-template v-html="resultTemplate"></atomic-result-template>
</atomic-result-list>
</template>
Using Custom Components
Writing your own components to leverage Vue components should be as straightforward as using Atomic components on any other page. However, it becomes a bit trickier when you want to use a custom Vue component inside your result templates which, as shown above, need to be passed down as raw HTML.
For example, the example project defines a ResultTextField
component, which takes a label
and a field
as props
and renders two Atomic components.
<!-- ResultTextField.vue -->
<script setup lang="ts">
const props = defineProps({
label: String,
field: String,
});
</script>
<template>
<atomic-text :value="props.label"></atomic-text>:
<atomic-result-text :field="props.field"></atomic-result-text>
</template>
If you tried to use this component in an HTML template as in the following snippet, and then tried to use it in the component further below it, Vue wouldn’t attempt to render it.
The reason is that Vue expects only native HTML to be passed via the
|
The solution is to import your component and define it as a custom element in the browser.
In your entry
|