JavaScript Search Framework component equivalences
JavaScript Search Framework component equivalences
These tables illustrate how out-of-the-box components in the Coveo Atomic, Headless and Quantic libraries match the functionality of various JavaScript Search Framework components. If there’s no direct mapping to a Headless, Atomic, or Quantic component, you’ll find a link to relevant documentation when possible. If a component is available in Headless but not in Atomic, you can still achieve the functionality by accessing Headless through Atomic. If a component is available in Headless but not in Quantic, you can still achieve the functionality by accessing Headless through Quantic.
Across Atomic, Headless, and Quantic, certain components or controllers offer the same functionality as two or more JavaScript Search Framework components.
For example, the Headless QuerySummary provides the same features as the JavaScript Search Framework QueryDuration and QuerySummary components.
How to read the equivalence tables
-
Identify the JavaScript Search Framework component you want to migrate.
-
Scan the tables by category to find the closest Atomic, Quantic, or Headless equivalent.
-
If you don’t find a direct mapping, open the "components not carried forward" section under the relevant category and follow the migration guidance.
|
|
Note
Mappings are identical across all categories and may appear more than once because they span UI, template, and navigation use cases. |
|
|
Disclaimer
The JavaScript Search Framework and Atomic both operate as UI libraries. In contrast, Headless provides controllers and actions with which you can create your own, custom components, even when using Atomic or Quantic. So, if no obvious Headless controller, Atomic or Quantic component matches the functionality you want to replicate from a JavaScript Search Framework component, you can often implement your own. |
Visual
Use this section to map visual and result-template components from the JavaScript Search Framework to their Atomic, Quantic, and Headless equivalents.
| JavaScript Search Framework | Atomic | Quantic | Headless |
|---|---|---|---|
None |
|||
None |
|||
Use Salesforce lightning component: |
None |
||
None |
None |
||
None |
|||
None |
|||
Visual components not carried forward
The following visual components from the JavaScript Search Framework don’t have direct equivalents in Headless, Atomic, or Quantic. If you require similar functionality, consider custom development or alternative approaches in your chosen framework.
| JSUI component | Migration guidance |
|---|---|
Use your framework’s modal or overlay components for similar UI effects.
The closest alternative is the |
|
Follows a similar rationale to the |
|
Follows a similar rationale to the |
|
Use custom result templates or table components in your framework.
The closest alternative is the |
|
This was used in JSUI to allow users to switch the result layout between list, card, or table. Use your framework’s UI controls to offer layout switching. Atomic supports multiple display types natively. |
|
Implement custom filter preference logic if needed, especially for power users or intranet scenarios. |
|
Use custom settings panels in your UI for advanced user control. |
|
Use your framework’s button components and bind them to search actions. |
|
Implement custom settings panels as needed, especially for advanced user scenarios. |
|
Implement sharing features using your framework’s capabilities. |
|
Atomic and Quantic use a different templating system than that of the legacy JSUI approach.
As a result, this artifact has no equivalent in the modern frameworks with Coveo.
See |
Functional
Use this section to map core search features such as facets, query input, sorting, and result management to their equivalents.
| JavaScript Search Framework | Atomic | Quantic | Headless |
|---|---|---|---|
None |
None |
||
None |
None |
||
None |
|||
None |
|||
Functional components not carried forward
The following functional components from the JavaScript Search Framework don’t have direct equivalents in Headless, Atomic, or Quantic. Consider custom logic or alternative approaches for these features.
| JSUI component | Migration guidance |
|---|---|
Use facet or group-by features in your framework to aggregate results as needed. |
|
Add follow or subscription features using custom logic or third-party integrations. |
|
Saw very little adoption, and relied on a now deprecated |
|
Atomic and Quantic support |
|
Implement notification or alerting features using your framework’s notification system or external integrations. |
|
Display alert messages using your framework’s notification or messaging components. |
|
Atomic uses a different templating system than that of the legacy JSUI approach. As a result, this artifact has no equivalent in the modern frameworks with Coveo. |
Navigation
Use this section to map navigation helpers such as tabs, pagination, refine toggles, and breadcrumbs to their equivalents.
| JavaScript Search Framework | Atomic | Quantic | Headless |
|---|---|---|---|
None |
|||
Navigation components not carried forward
The following navigation components don’t have direct equivalents in Headless, Atomic, or Quantic. Use your framework’s navigation controls or custom logic as needed.
| JSUI component | Migration guidance |
|---|---|
This was used in JSUI to allow users to switch the result layout between list, card, or table. Use your framework’s UI controls to offer layout switching. Atomic supports multiple display types natively. |
Notification and alert
Use this section to map attention and status indicators that highlight promoted or special results.
| JavaScript Search Framework | Atomic | Quantic | Headless |
|---|---|---|---|
None |
Notification and alert components not carried forward
The following notification and alert components don’t have direct equivalents in Headless, Atomic, or Quantic. Implement custom notification logic or use external systems as needed.
| JSUI component | Migration guidance |
|---|---|
Add follow or subscription features using custom logic or third-party integrations. |
|
Provide alerting or notification features using your framework’s notification system or external integrations. |
|
Display alert messages using your framework’s notification or messaging components. |
Analytics and tracking
Use this section to map analytics instrumentation and summary displays that help measure and explain query performance.
| JavaScript Search Framework | Atomic | Quantic | Headless |
|---|---|---|---|
Works in a similar way to Atomic |
|||
Analytics and tracking components not carried forward
The following analytics and tracking components don’t have direct equivalents in Headless, Atomic, or Quantic. Implement custom analytics logic or use external tracking systems as needed.
| JSUI component | Migration guidance |
|---|---|
A legacy system for query suggestions. This has now been replaced by Coveo’s machine learning model, so you should use the supported machine learning solution for your chosen framework. |
Template and layout
Use this section to map result template elements and layout helpers for rendering search results.
| JavaScript Search Framework | Atomic | Quantic | Headless |
|---|---|---|---|
None |
|||
None |
None |
||
None |
|||
None |
|||
None |
None |
||
None |
None |
||
Use Salesforce custom labels: |
None |
||
Use custom templates with: |
None |
||
Use custom templates with: |
None |
Template and layout components not carried forward
The following template/layout components don’t have direct equivalents in Headless, Atomic, or Quantic. Use your framework’s template and layout features for similar functionality.
| JSUI component | Migration guidance |
|---|---|
Use custom result templates or table components in your framework.
The closest alternative is the |
|
This was used in JSUI to allow users to switch the result layout between list, card, or table. Use your framework’s UI controls to offer layout switching. Atomic supports multiple display types natively. |
|
Atomic uses a different templating system than that of the legacy JSUI approach. As a result, this artifact has no equivalent in the modern frameworks with Coveo. |
Events
Shifting to a state-based architecture
The Coveo JavaScript Search Framework was built on an event-driven architecture where components interacted through a network of custom events.
Components listened for and emitted events such as querySuccess, buildingQuery, analyticsEventReady, facetSelectionChange, and more.
Each component was responsible for responding to these events and updating its own internal state or DOM accordingly.
Atomic, Quantic and the underlying Headless framework represent a fundamental shift in how Coveo’s search interfaces are structured. Rather than relying on events to coordinate component behaviour, these frameworks use a state-based architecture. All search-related data lives inside a shared, observable state managed by the Headless engine.
Components normally do not require developers to coordinate behavior by broadcasting events. Instead, you build against Headless actions and the shared observable state. When using Atomic or Quantic, you won’t interact with components by registering event listeners.
This shift brings Coveo’s modern frameworks in line with contemporary UI paradigms.
Why move to state-based architecture?
Moving from an event-driven approach to a state-driven one provides a number of advantages for both developers and end users.
Benefits of centralized state management
In an event-driven architecture you often need a hierarchical relationship between your components, as events only flow up. In centralized state management rather than tracking state on a component by component basis, Headless centralizes search state into one place. Each piece of state (query, facets, pagination, sorting, and results) is observable and predictable.
How modern frameworks improve maintainability
The Coveo JavaScript Search Framework components combined UI logic, state management, and event handling into a single layer.
Atomic and Quantic decouple these responsibilities with:
-
Headless managing state and business logic
-
Atomic and Quantic handling rendering and user interactions
This separation provides a stable Headless layer that developer teams can target with custom UIs instead of building directly against the underlying Search REST APIs. Headless exposes controllers, actions, and observable state you can reuse or extend. Since Headless is maintained and tested by Coveo developers, you typically avoid implementing state and business logic yourself when creating custom Atomic or Quantic components.
Testing in Atomic and Quantic
In the Coveo JavaScript Search Framework, testing custom behaviour often required simulating a series of events or mocking DOM interactions. With Headless, developers can instead focus their tests on pure state transitions by dispatching an action and asserting on the resulting state.
Extensibility through explicit patterns
Modern frameworks avoid the implicit and sometimes unpredictable chain of events seen in the Coveo JavaScript Search Framework. Developers can now extend behaviour, creating predictable and explicit behaviour that can scale with complexity by:
Mapping Coveo JavaScript Search Framework events
Developers coming from the Coveo JavaScript Search Framework may expect to find one-to-one equivalents similar to the table for UI components. Given the architectural shift, these events don’t translate directly to Atomic, Quantic, or Headless.
Instead of representing search flow with events, as mentioned in the previous section, these modern frameworks use state transitions.
A concept like "query success" is expressed as properties in the searchStatus or resultList controller states rather than as an event.
This distinction is essential, noting that events describe moments in time while state describes the entire search state consistently over time.
With event based architecture, you had to register an event listener for many events, like querySuccess, queryError, buildingQuery, newQuery, and more.
With state-based architecture, subscribing to the searchStatus means you’ll automatically be able to trigger code on every change of the Search Status, in one single place.
With this fundamental architectural difference in mind, attempting to map every Coveo JavaScript Search Framework event to their modern framework equals leads to inaccurate or misleading guidance. Instead, these events are best approached through patterns rather than specific event-by-event mappings.
Practical migration examples
The following examples illustrate how common Coveo JavaScript Search Framework event patterns translate to the modern, state-based architecture used in Atomic and Quantic. Each example pairs a legacy Coveo JavaScript Search Framework event pattern with its modern Atomic and Quantic equivalents. The Headless pattern is inherent in both frameworks, so no separate code sample is required.
Adding filters and context before a query
In the Coveo JavaScript Search Framework developers used the buildingQuery event to add constant filters or contextual metadata just before the query executed.
In Headless, this logic is handled directly by updating the engine’s state before the search request runs.
Coveo JavaScript Search Framework approach
document.addEventListener("DOMContentLoaded", function () {
Coveo.SearchEndpoint.configureSampleEndpointV2();
const root = document.querySelector("#search");
Coveo.$$(root).on("buildingQuery", function (e, args) {
args.queryBuilder.advancedExpression.add('@source==("Documentation")');
args.queryBuilder.addContext({
audience: "developer"
});
});
Coveo.$$(root).on("doneBuildingQuery", function (e, args) {
const aq = args.queryBuilder.advancedExpression.build();
});
Coveo.init(root);
});
Atomic equivalent
<script type="module">
const {
loadAdvancedSearchQueryActions,
loadContextActions
} = await import('@coveo/headless');
(async () => {
await customElements.whenDefined('atomic-search-interface');
const searchInterface = document.querySelector('atomic-search-interface');
await searchInterface.initialize({
accessToken: '<ACCESS_TOKEN>',
organizationId: '<ORGANIZATION_ID>'
});
const { engine } = searchInterface;
engine.dispatch(loadAdvancedSearchQueryActions(engine)
.updateAdvancedSearchQueries({aq: '@source==("Documentation")'})
);
engine.dispatch(loadContextActions(engine).addContext({
contextKey: 'audience',
contextValue: 'developer',
}));
searchInterface.executeFirstSearch();
})();
</script>
Dispatch a Headless advanced-query action to set a pre-query filter, aq.
The loadAdvancedSearchQueryActions call returns the possible action creators with the updateAdvancedSearchQueries call used to update the values of advanced search queries.
Together, these calls ensure the engine queries only the desired source before the first search. |
|
Add contextual metadata sent with each query.
The loadContextActions call returns the possible action creators with the addContext call used to then add the contextual metadata.
Use this for personalization, routing, or server-side ranking rules. |
Quantic equivalent
|
|
Note
To learn how Quantic exposes the Headless engine and how to implement custom components using Headless controllers, see Access Headless through Quantic. |
<c-quantic-search-interface engine-id="main" search-hub="Docs">
<c-my-init engine-id="main"></c-my-init>
</c-quantic-search-interface>
Create a custom LWC that retrieves the engine using its engine ID, and configures it by setting the aq value and any required context. |
// c/myInit/myInit.js
import {LightningElement, api} from 'lwc';
import { registerComponentForInit, initializeWithHeadless } from 'c/quanticHeadlessLoader';
export default class MyInit extends LightningElement {
@api engineId;
contextController;
connectedCallback() {
registerComponentForInit(this, this.engineId);
}
renderedCallback() {
initializeWithHeadless(this, this.engineId, this.initialize);
}
initialize = (engine) => {
engine.dispatch(
CoveoHeadless.loadContextActions(engine).addContext({
contextKey: 'audience',
contextValue: 'developer',
})
);
engine.dispatch(CoveoHeadless.loadAdvancedSearchQueryActions(engine)
.updateAdvancedSearchQueries({aq: '@source==("Documentation")'})
);
}
}
Once included in target pages, this component passes custom context when making search requests.
Handling "no results" and fatal query errors
The Coveo JavaScript Search Framework implementations listened to querySuccess and queryError and then manually toggled panels.
Modern implementations expose these conditions as state for Atomic and Quantic components to reference and affect render directly.
Coveo JavaScript Search Framework approach
document.addEventListener("DOMContentLoaded", function () {
Coveo.SearchEndpoint.configureSampleEndpointV2();
const root = document.querySelector("#search");
Coveo.$$(root)
.on("querySuccess", function (e, args) {
document.getElementById("error").style.display = "none";
document.getElementById("no-results").style.display = "none";
const results = args.results?.results || [];
if (results.length === 0) {
document.getElementById("no-results").style.display = "block";
}
})
.on("queryError", function (e, args) {
const errorElement = document.getElementById("error");
const message = args?.error?.message || "Search failed.";
errorElement.textContent = message;
errorElement.style.display = "block";
});
Coveo.init(root);
});
Atomic equivalent
Most pages need zero custom code for displaying and loading error states or no result cases. Atomic ships error and empty state components that already track the Headless status for you.
<atomic-search-interface>
<atomic-search-box></atomic-search-box>
<atomic-query-error></atomic-query-error>
<atomic-no-results></atomic-no-results>
<atomic-layout-section section="results">
<atomic-result-list></atomic-result-list>
</atomic-layout-section>
</atomic-search-interface>
The atomic-search-box wires user input to Headless search actions and integrates with Atomic stateful components. |
|
The atomic-query-error component handles fatal errors when performing a query on the index or Search API. |
|
The atomic-no-results component displays search tips and a "Cancel last action" button when there are no search results. |
|
The atomic-result-list component is responsible for displaying query results by applying one or more result templates. |
Quantic equivalent
Similarly to Atomic, Quantic provides built-in LWC components that react to Headless state for both fatal query errors and empty result scenarios.
<c-quantic-search-interface engine-id="main" search-hub="Docs">
<c-quantic-search-box engine-id="main"></c-quantic-search-box>
<c-quantic-query-error engine-id="main"></c-quantic-query-error>
<c-quantic-no-results engine-id="main"></c-quantic-no-results>
<c-quantic-result-list engine-id="main"></c-quantic-result-list>
</c-quantic-search-interface>
The c-quantic-search-interface initializes the Headless engine for child LWCs using the provided engine-id and search-hub. |
|
The c-quantic-query-error component listens to Headless state and displays fatal query errors without custom DOM mutation. |
|
The c-quantic-no-results component displays search tips and a "Cancel last action" button when there are no results. Any additional content embedded inside the component will be displayed as well. |
|
The c-quantic-result-list component is responsible for displaying query results by applying one or more result templates. |
Customizing result rendering
In the Coveo JavaScript Search Framework, developers often used post-render handlers to mutate result markup after it was rendered. Modern approaches perform the same customization by composing the final markup in result templates, producing predictable, testable output without brittle DOM edits.
Coveo JavaScript Search Framework approach
In the Coveo JavaScript Search Framework, you could use a newResultsDisplayed event handler to modify the appearance of each result in the result list.
In the following example, the handler changes the HTML of each rendered ResultLink component so that it displays the name of the source before the result title.
document.addEventListener("DOMContentLoaded", function() {
Coveo.SearchEndpoint.configureSampleEndpointV2();
root = document.querySelector("#search");
Coveo.$$(root).on("newResultsDisplayed", function(e, args) {
for (var i = 0; i < e.target.lastChild.children.length; i++) {
var currentResult = e.target.lastChild.children[i];
var formattedTitle = currentResult.CoveoResult.raw.source + " - " + currentResult.CoveoResult.Title;
currentResult.children[0].querySelectorAll("div.coveo-result-cell > a.CoveoResultLink").forEach(function(element) {
element.innerHTML = formattedTitle;
});
}
});
Coveo.init(root);
});
Modern frameworks tend to avoid post-render DOM edits and instead compose result templates that render the structure directly from state.
Atomic equivalent
With Atomic, this is carried out using the atomic-result-template as follows:
<atomic-search-interface>
<atomic-result-list fields-to-include="title,source">
<atomic-result-template>
<template>
<atomic-result-section-title>
<atomic-result-text field="source"></atomic-result-text>
<span aria-hidden="true"> - </span>
<atomic-result-link></atomic-result-link>
</atomic-result-section-title>
<atomic-result-section-bottom-metadata>
<atomic-result-printable-uri></atomic-result-printable-uri>
</atomic-result-section-bottom-metadata>
</template>
</atomic-result-template>
</atomic-result-list>
</atomic-search-interface>
Quantic equivalent
In Quantic, templates offer named slots. To implement functionality similar to that of the Coveo JavaScript Search Framework example, use a ResultField for source and ResultLink for title as follows:
|
|
Note
For more guidance on result templates and registration, see Result templates overview and Result template usage guide. |
<c-quantic-search-interface engine-id="main" search-hub="Docs">
<div onregisterresulttemplates={handleResultTemplateRegistration}>
<c-quantic-result-list engine-id="main"></c-quantic-result-list>
</div>
</c-quantic-search-interface>
Make use of a result template to determine the format of the individual result items in a search interface. The following HTML snippet showcases the result template that the JavaScript to follow will utilize as its result template.
// ./resultTemplates/resultTemplateExample.html'
<c-quantic-result-template>
<div slot="title" class="slds-truncate">
<c-quantic-result-field field="source"></c-quantic-result-field>
<span aria-hidden="true"> - </span>
<c-quantic-result-link engine-id="main"></c-quantic-result-link>
</div>
<div slot="bottom-metadata" class="slds-text-body_small">
<c-quantic-result-printable-uri></c-quantic-result-printable-uri>
</div>
</c-quantic-result-template>
The following JavaScript provides the logic for registering a template that displays the result source, title and additional metadata.
import resultTemplateExample from './resultTemplates/resultTemplateExample.html';
import {LightningElement, api} from 'lwc';
export default class MySearchPageExample extends LightningElement {
@api engineId = 'main';
@api searchHub = 'Docs';
const handleResultTemplateRegistration = (event) => {
event.stopPropogation();
const resultTemplatesManager = event.detail;
resultTemplatesManager.registerTemplates(
{
content: resultTemplateExample,
fields: ['source','title'],
},
);
};
}
Define the variables that you used in the HTML file, in your c-quantic-search-interface,and set their default values. |
|
The function that you attached to the onregisterresulttemplates event in your HTML file. As its name suggests, it lets you register your result templates. |
|
| Since you caught the event, stop its propagation. | |
| Register the result template on the result template manager. |
Conclusion: Migrating your search UI logic
The transition from the event-driven model of the Coveo JavaScript Search Framework to the state-based architecture of Atomic, Quantic, and Headless represents a shift not just in framework design but in developer mindset. Where developers once orchestrated behaviour by listening for and emitting events, they now work with actions and an observable, predictable state. This new paradigm leads to more maintainable codebases and better aligns developer experience with modern front-end patterns.
When planning a migration, focus on expressing your existing logic using controllers, actions, and state subscriptions rather than trying to map legacy events one-for-one.