Search bar
Open the widget from your store's search, with the shopper's search as their first question.
When a shopper submits a search, the widget opens and answers it: the search text becomes the shopper's first message, instead of a results page. Two methods connect a search to the widget:
- The HTML class: add
just-ai_search_barto the search<form>of a Shopify theme. No script to write. - JavaScript: dispatch the
just:ai-agent:openevent from your own code, for a custom theme, a headless storefront, or a search the theme handles in script.
You can also follow the setup from the dashboard, under Surfaces → Search bar: it gives a test link for the theme you edit and a message ready to send to your developer.
Before you start
- The widget is active on the theme you edit. Complete the Shopify installation first. Without the widget, a search runs as usual and nothing opens.
- Work on a copy of your live theme. Duplicate it in Shopify, edit the copy, test it, then publish it.
How it works
The widget listens to every form submit on the page. When the submitted form
carries the class just-ai_search_bar and contains an <input name="q">, the
widget:
- cancels the submit, so the browser stays on the page;
- opens the chat panel;
- sends the trimmed value of
qas the shopper's first message. An empty search opens the panel with no message.
The conversation is recorded with the source search_bar, so you find it under
Search bar in the dashboard's conversations.
The class never breaks the theme's search. The form submits normally to the search page whenever the widget cannot answer:
- the widget has not finished loading on the page;
- the store is in Preview and the browser did not open the test link;
- the store's plan has no conversations left.
Method 1: the HTML class
Exact class name
just-ai_search_bar: a hyphen between just and ai, an underscore between
ai and search_bar. just_ai_search_bar does nothing.
Open the theme code
In Shopify admin, go to Online Store → Themes. On the copy of your theme, select … then Edit code.
Find every search form
Search the code for routes.search_url, then for role="search". Each match on
a <form> element is a search form. A theme usually has several:
- the search in the header, often a dialog with predictive search;
- the search on the search results page;
- a search in the mobile menu, on some themes.
Some examples from Shopify's own themes:
| Theme | Header search | Search results page |
|---|---|---|
| Dawn | the form inside <predictive-search> | sections/main-search.liquid |
| Horizon | snippets/search-modal.liquid, see below | blocks/_search-input.liquid |
Other themes name their files differently: check your theme's search snippet.
When a snippet is rendered in several places ({% render 'search-form' %}),
one edit covers all of them.
Add the class to each form
Add just-ai_search_bar to the class attribute of the <form> element. Keep
every other attribute and the name="q" field as they are. In Dawn's header,
for example:
<!-- Before -->
<form
action="{{ routes.search_url }}"
method="get"
role="search"
class="search search-modal__form"
>
<input
class="search__input field__input"
id="Search-In-Modal"
type="search"
name="q"
/>
…
</form>
<!-- After -->
<form
action="{{ routes.search_url }}"
method="get"
role="search"
class="search search-modal__form just-ai_search_bar"
>
<input
class="search__input field__input"
id="Search-In-Modal"
type="search"
name="q"
/>
…
</form>A form with no class attribute takes class="just-ai_search_bar".
On the form, not around it
The class goes on the <form> element itself. Wrappers such as
<predictive-search> or <predictive-search-component role="search"> do not
count, even when they carry role="search".
Save, then test
Save the files, then test the copy of your theme.
Themes that handle Enter in script
The class acts on the form's submit. Some predictive search dialogs take over Enter in their own script once suggestions are showing, and go to the search page themselves.
Horizon's header dialog (snippets/search-modal.liquid, script
assets/predictive-search.js) is one of them. With the class on its form, Enter
opens the widget only while no suggestion is showing yet. Once suggestions
appear, Enter opens the search page, and only the button that shows all results
still opens the widget. On Horizon and on themes built the same way:
- add the class to the search results page form
(
blocks/_search-input.liquidon Horizon), which submits normally; - for the header dialog, take over Enter with the JavaScript method.
To spot such a dialog, type a search in it on the preview, wait for the suggestions, then press Enter: if the search page opens, its script handles Enter.
Method 2: JavaScript
Dispatch just:ai-agent:open on window, with the search as message and
search_bar as source:
window.dispatchEvent(
new CustomEvent('just:ai-agent:open', {
detail: { message: query, source: 'search_bar' },
}),
);message: the search text. It is sent as the shopper's first message as soon as the session is ready. Leave it empty to open the panel only.source: keepsearch_bar. It records the conversation as a search bar conversation in the dashboard.
An event dispatched while the widget is still loading is kept and replayed once it is ready. Only the latest early event is kept.
Keep the search working without the widget
Nothing listens to the event when the widget is not on the page, and the widget ignores it when the store's plan has no conversations left. A handler that cancels the search first leaves the shopper with nothing. Check that the widget is registered before you cancel, as in the examples below, and use the HTML class wherever the form submits normally: it falls back to the theme's search on its own.
Connect a custom search form
For a custom theme or a search form you render yourself:
<form id="site-search" action="/search" method="get" role="search">
<input type="search" name="q" aria-label="Search" />
<button type="submit">Search</button>
</form>
<script>
function openWidget(query) {
window.dispatchEvent(
new CustomEvent('just:ai-agent:open', {
detail: { message: query, source: 'search_bar' },
}),
);
}
const form = document.getElementById('site-search');
form.addEventListener('submit', (event) => {
if (!customElements.get('just-ai-agent')) return;
event.preventDefault();
openWidget(form.elements.q.value.trim());
});
</script>Headless storefronts
The same event works in any framework. In React, for example:
function SearchForm() {
function onSubmit(event: React.FormEvent<HTMLFormElement>) {
if (!customElements.get('just-ai-agent')) return;
event.preventDefault();
const query = new FormData(event.currentTarget).get('q');
window.dispatchEvent(
new CustomEvent('just:ai-agent:open', {
detail: { message: String(query ?? '').trim(), source: 'search_bar' },
}),
);
}
return (
<form action="/search" role="search" onSubmit={onSubmit}>
<input type="search" name="q" aria-label="Search" />
</form>
);
}The widget must be installed on the storefront: see the headless installation.
Take over Enter in a predictive search dialog
For a dialog whose script handles Enter, listen to keydown on the document in
the capture phase, so your listener runs before the theme's. This example acts
on forms that carry the class, and leaves Enter to the theme when the shopper
has selected a suggestion with the arrow keys:
document.addEventListener(
'keydown',
(event) => {
if (event.key !== 'Enter' || event.isComposing) return;
const input = event.target;
if (!(input instanceof HTMLInputElement) || input.name !== 'q') return;
const form = input.closest('form.just-ai_search_bar');
if (!form || form.querySelector('[aria-selected="true"]')) return;
if (!customElements.get('just-ai-agent')) return;
event.preventDefault();
event.stopPropagation();
window.dispatchEvent(
new CustomEvent('just:ai-agent:open', {
detail: { message: input.value.trim(), source: 'search_bar' },
}),
);
},
true,
);Themes differ in how they mark the selected suggestion: check yours and adapt the selector, then test every key path on the preview.
Test it
Open the store with the test link from the dashboard (Surfaces → Search bar). It carries two parameters:
just_ai_tester=1marks your browser as a test browser. While the store is in Preview, the widget loads only in a marked browser, and the conversations you start there count as tests, not as shoppers.preview_theme_id=…opens the theme copy you edit, when it is not your live theme.
You can also build it by hand:
https://your-store.com/?preview_theme_id=123456789&just_ai_tester=1.
On that page, check each search form:
- Type a search, then press Enter. The chat panel opens with your search as
the first message, and the page does not move to
/search. - Do the same with the search button, on desktop and on mobile.
- Submit an empty search: the panel opens with no message.
To count the marked forms, run this in the browser console:
document.querySelectorAll('form.just-ai_search_bar').length;Limits and troubleshooting
| Symptom or case | What happens and what to do |
|---|---|
| A search app (Boost, Searchanise…) runs the search | The app replaces the theme's form or sends the search in its own script, so the class is never reached. Use the JavaScript method if the app lets you hook its search; otherwise, contact us. |
| The search page still opens on Enter | The class is misspelled, sits on a wrapper instead of the <form>, or the theme handles Enter in script. |
The form has no field named q | The widget reads the search from <input name="q"> and lets any other form submit normally. Keep the theme's name="q". |
A script submits the form with form.submit() | That call fires no submit event, so the class cannot catch it. Use the JavaScript method in that script. |
| The shopper selects a suggestion with the keyboard | The theme's predictive search handles it, and themes differ. Test the arrow keys and Enter on the preview. |
| You updated or changed the theme | A new theme version, or another theme, does not carry your edit. Add the class again. |
| The store is in Preview | Only a browser that opened the test link loads the widget. Every other visitor gets the theme's search. |
| The plan has no conversations left | The widget stays hidden and the class lets the search run. A dispatched event is ignored. |
| A search submitted while the page is still loading | The widget catches searches once it has loaded. An earlier search runs as usual. |
Going further
- Events reference: the full
just:ai-agent:opencontract. - Trigger button: open the widget from any button or link.
- Analytics: the metrics behind the
search_barsource.