Coveo has come a long way since the days of the JavaScript UI (JSUI). It’s highly recommended that if you’re using the JSUI that you consider implementing alternatives. When it comes to Coveo for Sitecore, you’ve been able to use JSUI but you’ve also been able to take advantage of Hive. Personally, I’ve always been a fan of JSUI. Thanks to utilizes like Coveo Turbo, being able to quickly spin up a JSUI interface to debug a pipeline or assist a client with some design issues. It was always my go-to. And while technically you still can use both, I’d look at implementing either Coveo Headless or Coveo Atomic to future-proof your solution.
Both offerings have made improvements in their architecture, implementation and performance considerably in the last year. If you check out the release notes of each, it’s obvious that there is considerable investment behind them. Therefore both are considerably safe choices when looking to implement as part of your next project.
But which is the best fit? Is there one? Let’s break down what each one offers, its benefits and limitations, and see if we can help you make a solid choice.
One thing I will mention, these offerings are very similar in that while Coveo Headless is a platform of sorts, Coveo Atomic is BUILT on Headless. It just gets you from A to B, that much faster.
Coveo Headless
So, first, what is Headless? Well, in short, it’s the library that you use to build Coveo applications on top of. As I stated above, Coveo Atomic is built using Coveo Headless.
Below is an example of what Coveo Headless, when combined with Material UI, can look like, but I’ll emphasize you can make it whatever you want.
There is one fundamental piece that makes Headless work, and that’s the engine. The engine’s state is constantly being updated while a user interacts with various features in the application.
Under the hood of Coveo Headless are its interactions with the Coveo APIs, such as the Coveo Search API, Platform API, REST API, etc. Just to name a few. If you want a complete view of that immense world, check out the API Overview.
I’ve got a comparison below detailing the pros and cons of using Headless, but you have to look at what you’re building. Are you building a solution that for example, may not look like a traditional search page? Are you building a site that you want to seamlessly add results based on the content on a particular page? These are the kinds of things you can build with Headless quite simply.
Installing Headless
Make sure you have NPM and Node (> v12) and are building a project (hopefully TypeScript) that is based upon React, Angular, NextJs or Vue.js.
Run the following command:
npm install @coveo/headless
Setting Up
Moving past the install, the next question will be, what type of engine are you going to be working with? Are you working with:
- Search Engine
- Recommendation Engine
- ProductRecommendation Engine
- ProductListing Engine
- CaseAsist Engine, or
- Insight Engine
Each has a very different purpose and each has a specific set of components that you would work with.
Let’s say you wanted to start off with the Search Engine as you want to create a search interface and result of your site. Everything revolves around the engine. The state, events, all of it. Locate the Engine.ts
file, open it, and update the contents with the following code:
import { buildSearchEngine, getOrganizationEndpoints } from '@coveo/headless';
export const headlessEngine = buildSearchEngine({
configuration: {
organizationId: '<ORG_ID>',
organizationEndpoints: getOrganizationEndpoints('<ORG_ID>')
accessToken: '<ACCESS_TOKEN>',
renewAccessToken: <CALLBACK>,
}
});
If you were going to work with the Recommendation Engine because you’re trying to create components that provide content based upon a user’s journey, then you’d replace this file with the following code:
import { buildRecommendationEngine, getOrganizationEndpoints } from '@coveo/headless';
export const headlessRecommendationEngine = buildRecommendationEngine({
configuration: {
organizationId: '<ORG_ID>',
organizationEndpoints: getOrganizationEndpoints('<ORG_ID>')
accessToken: '<ACCESS_TOKEN>',
renewAccessToken: <CALLBACK>,
}
});
You can see where this is going. Each component you build will utilize these types of engines to access the platform. And each engine type has it’s own appropriate components.
Some components can be used across various engines; some are specific to that engine. That said, this is Headless, so there’s nothing saying you can’t create your own components that utilize various ones altogether.
Component Breakdown
When it comes to breaking down the component list, you’ll need to understand that, unlike Atomic, you’re going to be interacting with both controllers and if you desire even further control, then there are actions for each component. There are forty some controllers and thirty some actions.
For example, capturing analytics when working with the Recommendation engine might look something like this when trying to capture when a recommendation item that has been displayed has been clicked on.
const logClick = (recommendation: Result) => {
if (!engine) {
return;
}
<span class="hljs-keyword">const</span> { logRecommendationOpen } = loadClickAnalyticsActions(engine);
engine.dispatch(logRecommendationOpen(recommendation));
};
Customizing Components
The beauty with these are you can use plain CSS or you can throw in some Tailwind. Really the choice is yours. Here’s what a simple component might look like in Headless if you were to use Tailwind for styling.
import { HeadlessFacetProps } from './Facet';
import Facet from './Facet';
export interface FacetProps {
facets: HeadlessFacetProps[];
}
const Facets = (props: FacetProps): JSX.Element => {
return (
<div
id="js-searchpage-facets"
className="hidden absolute top-2 p-1 pb-0 sm:p-0 border border-nu-grey-dark sm:border-0 w-full sm:top-auto bg-white sm:relative sm:flex sm:flex-col"
>
{props.facets.map((facet, index) => {
return (
<div key={index}>
<Facet {...facet} />
</div>
);
})}
</div>
);
};
export default Facets;
Now take that concept and expand it to every other component you need to implement and you get an idea on the scale. There’s a lot of moving parts. Each component works and reacts differently depending upon the state
of the engine.
Don’t be discouraged though. Once you’ve got a working Headless setup, customizing it for each client is a lot simpler and you’ll find you’re reusing components throughout different builds.
Coveo Atomic
Alternatively, Coveo Atomic is a component-based solution enabling businesses and individuals a way of quickly going from A to Z without considerable development required. Unlike Headless where you work directly with the controllers and actions of a component, in *Atomic,* you strictly work with the component itself.
Installing Atomic
There are two methods for utilizing **Atomic**. It really depends on how are planning on integrating the search components. Are they apart of a larger application or are they separate? Are you trying to do something quick or as a final product?
NPM
If you’re building a headless application or is part of a larger framework, going the NPM route is probably you’re safe bet.
npm install @coveo/atomic
CDN
Without a doubt, if you’re needing a search page quick, and put it up somewhere to show a client quick results, then use the CDN approach. With a Coveo source and API key pre-configured you can have results showing up in less than 10 minutes.
<script type="module" src="https://static.cloud.coveo.com/atomic/v2/atomic.esm.js"></script>
<link rel="stylesheet" href="https://static.cloud.coveo.com/atomic/v2/themes/coveo.css"/>
Setting Up
At the root of Atomic’s structure is the <atomic-search-interface>
component. This will look something like this:
<body>
<atomic-search-interface id="abc-company-search">
<atomic-search-box></atomic-search-box>
...
</atomic-search-interface>
</body>
From here on you will assemble the rest of the components. Just like the engine in Headless, you need to initialize your search. So let’s do that now, reference a JavaScript file in your HTML and use the code below. Given your access token is visible, I highly recommend connecting the access token to a search hub and having that search hub control (via a condition) which Query Pipeline to run. This limits your organizations exposure.
(async () => {
await customElements.whenDefined('atomic-search-interface');
const searchInterface = document.querySelector('#abc-company-search');
await searchInterface.initialize({
accessToken:'<ACCESS_TOKEN>',
organizationId: '<ORG_ID>'
organizationEndpoints: await searchInterface.getOrganizationEndpoints('<ORG_ID>'),
});
searchInterface.executeFirstSearch();
})();
Components Available for Atomic
There are presently fifty (50) components available for Coveo Atomic. You can see the full list of Atomic components here. While that seems like an extraordinary number, you should understand that a lot of the components are utilized in combination with one another. Just like in Headless.
If you’d like to see a complete working example, Coveo has put together a working Atomic example.
Customizing Coveo Atomic
I could see, as having experienced it in its infancy, styling can be a stumbling block when it comes to Coveo Atomic. It is, after all not the typical way to stylize and customize how a component looks. This is largely because many of the components come pre-styled, and some utilize the shadow DOM. That doesn’t prevent the components from being customized entirely, but you’re forced to use “parts,” aka the::part()
pseudo-element. The other is by taking advantage of the themes. Comparing it to JSUI, it protects components and ensures they continue working during the customization process. You can find these parts by inspecting the rendered DOM and look for #shadow-root
and then inside the elements within, you’ll find part
and the values are used as selectors. Here’s an example:
atomic-search-box::part(wrapper) {
justify-content: left;
}
What are themes? Well, they’re just a set of variables that allow you to customize the attributes of each component. You do this by updating the :root pseudo-class. For example:
:root {
--atomic-primary: #787878;
}
Those variables can then be used within parts like so:
background-color: var(--atomic-neutral);
Read the full list of Atomic Themes here.
Other times, for example, the atomic-result-template, we need to put styles within the tag. Styles entered inside the template affect the template. If you were to add the same style in the stylesheet, it wouldn’t affect the template.
<atomic-result-list>
<atomic-result-template>
<template>
<style>
// custom styling here
</style>
<atomic-result-section-title>
<atomic-result-link></atomic-result-link>
</atomic-result-section-title>
</template>
<atomic-result-template>
<atomic-result-list>
Comparing Headless and Atomic
FEATURE | COVEO HEADLESS | COVEO ATOMIC |
---|---|---|
Search Algorithms | Pros: Ability to customize the search experience, queries, and filtering through APIs, customizable for complex use cases.
Cons: Requires considerable development startup. But, once you’ve got a base done, each project thereafter can leverage what you’ve learned and built. | Pros: Pre-built algorithms optimized for personalized search, easy to implement.
Cons: Less customizable for complex use cases. |
Customization | Pros: Using APIs and SDKs, highly configurable search experiences can produce personalized search solutions.
Cons: Demands more technical know-how and development work. | Pros: Benefits include a quicker time to market and pre-built search components that can be integrated and altered.
Cons: Little opportunity to design original search experiences. |
Personalization | Pros: Customization options that can be tailored to corporate needs are accessible through APIs. You’re able to mix and match solutions to meet your need.
Cons: Implementation requires extra development work and is prone to needing routine maintenance. | Pros: Machine learning-based personalization features are built-in and simple to use.
Cons: It may not be as customizable for sophisticated personalization use cases. |
Analytics and Reporting | Pros: Easy to set up, robust built-in analytics and reporting features. Customizable to corporate needs, analytics and reporting tools are available through the Platform.
Cons: Implementation requires extra development work. | Pros: Easy to set up, robust built-in analytics and reporting features. Customizable to corporate needs, analytics and reporting tools are available through the Platform.
Cons: Minimal personalization for certain business requirements. |
Integration | Pros: API-first approach for easy integration into any technology stack, supports various platforms and languages.
Cons: Requires more development effort to implement. | Pros: Pre-built components provide less flexibility but a quicker time to market, which is appropriate for simple search experiences.
Cons: Minimal personalization for certain business requirements. |
Flexibility | Pros: Highly flexible and modular solutions for creating custom search experiences can meet unique business needs. Cons: Requires more development effort to implement.
Con: All the flexibility can come at the cost of routine maintenance. | Pros: Less flexibility but faster time-to-market with pre-built components, good for simple search experiences.
Cons: Limited customization for unique business needs. |
In Summary
Having worked with both of the above, it really comes down to a fit for purpose. Headless is by far the most powerful and customizable of the two and what we tend to lean to when incorporating Coveo into a custom front-end application for a client. Atomic is absolutely fantastic, going from nothing to a fully-fledged solution in nothing flat and perfect for delivering a proof-of-concept to a client to demonstrate what their content could look like, driven by Coveo.