Use the React wrapper
Use the React wrapper
This is for:
DeveloperThe integration of JSX in React projects with Atomic web components can be tricky. Atomic React is a wrapper around the core Atomic library meant to address this issue.
Since Atomic React is built on top of the core Atomic component library, most concepts that apply to the core Atomic library apply directly to Atomic React. The goal of this article is to go over the few areas where the use of Atomic React differs from the use of the core Atomic library.
For a concrete example you may want to start from or refer to throughout this article, see this Atomic React page. |
Install Atomic React
NPM
Install Atomic React using the npm package.
npm i @coveo/atomic-react
Note
If you use TypeScript, note that Atomic React doesn’t support the |
CDN
The library is also available via the Coveo CDN as an IIFE (Immediately Invoked Function Expression).
We recommend this approach only for quick prototyping and testing, as it requires very minimal front-end tooling and build tools. We recommend against using the IIFE approach in production. Browsers have to download the entire library code, regardless of which components are actually used. We rather recommend using a bundler in this situation (for example Webpack).
You can read more about this approach below.
Static Assets
For performance reasons, the generated Atomic React JavaScript bundle does not 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 React 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.
For example, for any project created with Create React App, this would mean copying language and icon assets to the ./public
directory.
cp -r node_modules/@coveo/atomic-react/dist/assets public/assets
cp -r node_modules/@coveo/atomic-react/dist/lang public/lang
Note
Be sure to respect the folder hierarchy, with SVG icons under the |
Result Templates
The way to define result templates for an HTML project using the core Atomic library involves defining one or multiple atomic-result-template
components, configured with HTML properties, adding conditions on the attributes and metadata of each result.
Coupled with the <template>
HTML tag, this approach works very well in a plain HTML project.
However, it can be limiting and awkward to use in a React project using JSX.
Atomic React exposes atomic-folded-result-list
, atomic-result-list
, and atomic-search-box-instant-results
components with a template
property that can be used in a more straightforward manner when coupled with JSX.
The template
property accepts a function with a Result
parameter in the case of the atomic-result-list
and atomic-search-box-instant-results
components, and a FoldedResult
in the case of the atomic-folded-result-list
component.
Use those parameters to conditionally render different templates based on properties and fields available in result items.
The template
function must then simply return a valid JSX Element.
The following is an example of how to create a fictitious search page with predefined templates for YouTube videos and Salesforce cases:
const MyResultTemplateForYouTubeVideos: React.FC<{result: Result}> = ({
result,
}) => {
return (
<>
<AtomicResultSectionVisual>
<AtomicResultImage field="ytthumbnailurl" />
</AtomicResultSectionVisual>
<AtomicResultSectionTitle>
<AtomicResultLink />
</AtomicResultSectionTitle>
{result.raw.ytvideoduration !== undefined && (
<AtomicResultSectionBottomMetadata>
<AtomicText value="Duration" />
<AtomicResultNumber field="ytvideoduration">
<AtomicFormatUnit unit="minute" />
</AtomicResultNumber>
</AtomicResultSectionBottomMetadata>
)}
</>
);
};
const MyResultTemplateForSalesforceCases: React.FC<{result: Result}> = ({
result,
}) => {
return (
<>
<AtomicResultSectionTitle>
<AtomicResultLink />
</AtomicResultSectionTitle>
<AtomicResultSectionExcerpt>
<AtomicResultText field="excerpt" />
</AtomicResultSectionExcerpt>
<AtomicResultSectionEmphasized>
{result.raw.sfpriority !== undefined && (
<>
<AtomicText value="Priority" />
<AtomicResultText field="sfpriority" />
</>
)}
{result.raw.sfstatus !== undefined && (
<>
<AtomicText value="Status" />
<AtomicResultText field="sfstatus" />
</>
)}
</AtomicResultSectionEmphasized>
</>
);
};
const MyDefaultTemplate: React.FC<{}> = () => {
return (
<>
<AtomicResultSectionTitle>
<AtomicResultLink />
</AtomicResultSectionTitle>
<AtomicResultSectionExcerpt>
<AtomicResultText field="excerpt" />
</AtomicResultSectionExcerpt>
</>
);
};
const MyResultTemplateFunction = (result: Result) => {
if (result.raw.filetype === 'YoutubeVideo') {
return <MyResultTemplateForYouTubeVideos result={result} />;
}
if (result.raw.objecttype === 'Case') {
return <MyResultTemplateForSalesforceCases result={result} />;
}
return <MyDefaultTemplate />;
};
const MyPage = () => {
const engine = buildSearchEngine({
configuration: getSampleSearchEngineConfiguration(),
});
return (
<AtomicSearchInterface engine={engine}>
<AtomicResultList template={MyResultTemplateFunction} />
</AtomicSearchInterface>
);
};
Use the template property of the atomic-result-list component to invoke your result template function in your search interface. |
Result Template Component Styling
Due to the way Atomic web components use Shadow Dom and CSS parts to provide encapsulation, you must follow the guidelines below to style elements inside any result template.
Higher-Order Components
This approach consists in wrapping any core Atomic component inside a styled one, which you can then reuse in one or more result templates. You can do so by using inline styling as shown in the example below, or using more advanced techniques such as CSS modules.
This option works well if you don’t have to create any CSS rule that would target the shadow parts of an Atomic result component.
The following example sets the color of all result links in a template to pink.
const MyStyledResultLink: React.FC<
React.ComponentProps<typeof AtomicResultLink>
> = (props) => {
return (
<AtomicResultLink {...props} style={{color: 'pink'}}>
{props.children}
</AtomicResultLink>
);
};
const MyPage = () => {
const engine = buildSearchEngine({
configuration: getSampleSearchEngineConfiguration(),
});
return (
<AtomicSearchInterface engine={engine}>
<AtomicResultList
template={(result) => {
return <MyStyledResultLink />;
}}
/>
</AtomicSearchInterface>
);
};
This line allows you to extract all props exposed by the AtomicResultLink component and to reuse them in your higher-order component.
You could also extend those props. |
|
Use the template property of the atomic-result-list component to display your higher-order component. |
<style>
Tag
Using <style>
tags works in all scenarios, and allows you to target any Shadow parts that an Atomic result component exposes, similarly to what you would do using plain HTML.
The following is an example that sets the text color of an AtomicResultBadge
to pink:
const myStyles = `
atomic-result-badge::part(result-badge-element) {
color: pink;
}
`;
const MyPage = () => {
const engine = buildSearchEngine({
configuration: getSampleSearchEngineConfiguration(),
});
return (
<AtomicSearchInterface engine={engine}>
<AtomicResultList template={(result)=> {
<style>{myStyles}</style>
<AtomicResultBadge />
}} />
</AtomicSearchInterface>
);
};
Use the template property of the AtomicResultList component to display your styled component. |
JSX
Rather than accepting JSON as in the core Atomic library, the following Atomic React component properties support JSX:
-
atomic-facet
allowed-values
-
atomic-category-facet
base-path
-
atomic-search-interface
fields-to-include
-
atomic-recs-interface
fields-to-include
Example:
<AtomicFacet allowedValues={["in progress", "completed"]}></AtomicFacet>
Localization (i18n)
The Atomic React search interface component exposes an optional localization
option, which takes a callback function that lets you handle localization.
<AtomicSearchInterface
localization={(i18n) => {
i18n.addResourceBundle('en', 'translation', {
search: "I'm feeling lucky!",
});
}}
></AtomicSearchInterface>
Usage with CDN Scripts (IIFE)
To use the IIFE approach, you need to pull different scripts in the page in the correct order, using proper external dependencies with matching versions.
First, identify the version of @coveo/atomic
that is used by @coveo-atomic-react
.
You can do that by running npm view @coveo/atomic-react
, and then identifying the @coveo/atomic
dependency:
> npm view @coveo/atomic-react
@coveo/atomic-react@1.23.11 | Proprietary | deps: 1 | versions: 209
React specific wrapper for the Atomic component library
https://github.com/coveo/ui-kit#readme
dist
.tarball: https://registry.npmjs.org/@coveo/atomic-react/-/atomic-react-1.23.11.tgz
.shasum: 64ba0a5f686e3638b1180910f6fed5e4980bd9e2
.integrity: sha512-/qI5O7SWNinBYWAedRW62ko0oF4c/hfapgqKe9xSq/NJfg+KBzcR3SbdyhpaUAWpPX1910GP9cMyr4wf8irJnQ==
.unpackedSize: 771.1 kB
dependencies:
@coveo/atomic: 1.108.2
In the above example, you can see that at the time of this writing, the current latest version of @coveo/atomic-react
is 1.23.11
, and that it is using @coveo/atomic
at version 1.108.2
.
Then, you need to do the same to find the matching version of @coveo/headless
used by the target @coveo/atomic
version.
Using the above example with @coveo/atomic@1.108.2
:
> npm view @coveo/atomic@1.108.2
@coveo/atomic@1.108.2 | Apache-2.0 | deps: 15 | versions: 728
A web-component library for building modern UIs interfacing with the Coveo platform
https://docs.coveo.com/en/atomic/latest/
dist
.tarball: https://registry.npmjs.org/@coveo/atomic/-/atomic-1.108.2.tgz
.shasum: 6e844073f55f10328b4b828c0190b8fe31403fbf
.integrity: sha512-LhFR6k7NdGi8pYHmWOq3gM3rE0zeTKc6biTa1Z+ZKr5TEXIr97rpxi1HSnViQUowQYYCcY44dvsAYz2yyPvXTA==
.unpackedSize: 87.1 MB
dependencies:
@coveo/bueno: 0.42.1 @stencil/store: 1.5.0 focus-visible: 5.2.0
@coveo/headless: 1.103.3 coveo-styleguide: 9.34.4 i18next-http-backend: 1.4.1
@popperjs/core: ^2.11.6 dayjs: 1.11.5 i18next: 20.6.1
@salesforce-ux/design-system: ^2.16.1 dompurify: 2.3.10 stencil-inline-svg: 1.1.0
@stencil/core: 2.17.3 escape-html: 1.0.3 ts-debounce: ^4.0.0
In the above example, you can see that @coveo/atomic@1.108.2
has the dependency @coveo/headless@1.103.3
.
To summarize, as of the time of this writing, the versions to keep in mind are the following:
-
@coveo/atomic-react
→ 1.23.11 -
@coveo/atomic
→ 1.108.2 -
@coveo/headless
→ 1.103.3
This means you would import Atomic React as follows:
<head>
<!-- React and ReactDOM need to be included -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<!-- Optional script, which allows to use JSX directly in an inline script in the page -->
<script crossorigin src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- @coveo/headless needs to be included as a dependency -->
<!-- Note the matching major and minor versions as explained above -->
<script crossorigin src="https://static.cloud.coveo.com/headless/v1.103/headless.js"></script>
<!-- @coveo/atomic needs to be included as a dependency -->
<!-- Note the matching major and minor versions as explained above -->
<script crossorigin type="module" src="https://static.cloud.coveo.com/atomic/v1.108/atomic.esm.js"></script>
<!--And then finally @coveo/atomic-react is included -->
<!-- Note the matching major and minor versions as explained above -->
<script crossorigin src="https://static.cloud.coveo.com/atomic-react/v1.23/iife/atomic-react.min.js"></script>
</head>
Once this is done, you can start using CoveoAtomicReact
directly with an inline script tag:
<!DOCTYPE html>
<html>
<head>
<!-- ... -->
</head>
<body>
<div id="container"></div>
</body>
<script type="text/babel">
'use strict';
class SearchPage extends React.Component {
constructor(props) {
super(props);
// Configure engine
this.engine = CoveoAtomicReact.buildSearchEngine({...});
}
render() {
return (
<CoveoAtomicReact.AtomicSearchInterface engine={this.engine}>
<CoveoAtomicReact.AtomicSearchBox></CoveoAtomicReact.AtomicSearchBox>
<CoveoAtomicReact.AtomicResultList
template={MyTemplateFunction}
></CoveoAtomicReact.AtomicResultList>
<!-- ... -->
</CoveoAtomicReact.AtomicSearchInterface>
);
}
}
const domContainer = document.querySelector('#container');
const root = ReactDOM.createRoot(domContainer);
root.render(React.createElement(SearchPage));
</script>
</html>
Reference
All components available in the core Atomic library are available in the Atomic React wrapper. Additionally, the following Atomic React components expose options not available in the equivalent core Atomic components.
atomic-search-interface
In addition to the properties and methods available in the core atomic-search-interface
component, the Atomic React atomic-search-interface
component exposes the following properties:
Property | Attribute | Description | Type | Default |
---|---|---|---|---|
|
|
An optional callback that lets you control the interface localization. The function receives the search interface i18n instance, which you can then modify (see Localization). |
|
|
|
|
An optional callback function that can be used to control the execution of the first query. |
|
If not provided, a default function will be used, which executes the first query immediately after initialization. |
Also, the following two properties exposed through the core atomic-search-interface
component are deprecated in the Atomic React atomic-search-interface
component.
Using them would have no effect.
-
pipeline
-
searchHub
Rather, set the pipeline and search hub in the target engine
search
configuration.
For example:
const MyPage = () => {
const engine = buildSearchEngine({
configuration: {
accessToken: 'xxc23ce82a-3733-496e-b37e-9736168c4fd9',
organizationEndpoints: getOrganizationEndpoints('electronicscoveodemocomo0n2fu8v'),
organizationId: 'electronicscoveodemocomo0n2fu8v',
pipeline: 'Search',
searchHub: 'MainSearch',
},
});
return (
<AtomicSearchInterface engine={engine}>
{/* ... */}
</AtomicSearchInterface>
);
};
atomic-folded-result-list
In addition to the properties and methods available in the core atomic-folded-result-list
component, the Atomic React atomic-folded-result-list
component exposes the following property:
Property | Attribute | Description | Type | Default |
---|---|---|---|---|
|
|
A template function that takes a folded result item and outputs its target rendering as a JSX element. It can be used to conditionally render different types of result templates based on the properties of each result. |
|
atomic-result-list
In addition to the properties and methods available in the core atomic-result-list
component, the Atomic React atomic-search-interface
component exposes the following property:
Property | Attribute | Description | Type | Default |
---|---|---|---|---|
|
|
A template function that takes a result item and outputs its target rendering as a JSX element. It can be used to conditionally render different types of result templates based on the properties of each result. |
|
atomic-search-box-instant-results
In addition to the properties and methods available in the core atomic-search-box-instant-results
component, the Atomic React atomic-search-box-instant-results
component exposes the following property:
Property | Attribute | Description | Type | Default |
---|---|---|---|---|
|
|
A template function that takes a result item and outputs its target rendering as a JSX element. It can be used to conditionally render different types of result templates based on the properties of each result. |
|