AI Search Classic Contensis Implementation
Log in to add to favouritesPage last updated 05 October 2026
Add AI Search to a classic Contensis site, or any site not built with React, with one script tag and one HTML element.
The Web Component gives you the search dialog. Inline answers above search results, answer feedback and reCAPTCHA are currently only in the React components.
1. Add the script
<script src="https://unpkg.com/insytful-ai-search-components@4/dist/insytful-search.js"></script>@4 gets the latest 4.x release. To control exactly when your site picks up updates, pin a full version instead, for example @4.3.0.
2. Add the markup
@{
const string InsytfulAISearchTheme = @"
.insytful-theme {
--insytful-font-family: 'Helvetica Neue', Arial, sans-serif;
--insytful-brand-primary: #da3949;
--insytful-btn-icon-search-bg-default: #da3949;
--insytful-btn-icon-search-bg-hover: #bf2f3d;
}
";
}
<style>
insytful-search:not(:defined) { display: none; }
</style>
<insytful-search
id="ai-search"
api-uri="@CurrentContext.Site.AI.Endpoint"
project-id="@CurrentContext.Site.AI.ProjectId"
theme="@InsytfulAISearchTheme"
>
<button slot="trigger">Search this site</button>
<span slot="title">Ask our AI</span>
<span slot="description">Get instant answers about our courses and services</span>
<span slot="disclaimer">AI responses may not always be accurate. Please check important information.</span>
<img slot="avatar" src="/logo.png" alt="" width="32" height="32" />
<insytful-close></insytful-close>
<insytful-suggestion>How do I apply?</insytful-suggestion>
<insytful-suggestion>What courses do you offer?</insytful-suggestion>
<insytful-mode name="ai">AI Search</insytful-mode>
<insytful-mode name="classic" path="/search/index.aspx?search_keywords=">Classic Search</insytful-mode>
</insytful-search>Your account manager provides the values for api-uri and project-id. In Razor, prefix them with @ so they're evaluated rather than printed as text.
- The
:not(:defined)rule hides the element until the script loads, so nothing flashes on screen. idis optional. You need one if you plan to control the dialog from your own JavaScript.- Child elements (
<insytful-suggestion>,<insytful-mode>and so on) are read once when the page loads. They must be in the server-rendered HTML, not added later by script. - The classic
<insytful-mode>is optional. Include it to let visitors switch to your existing search results. The tabs are created for you.
3. Connect your existing search box (optional)
If your header already has a search box, you can send its query to the AI dialog:
customElements.whenDefined('insytful-search').then(() => {
const element = document.getElementById('ai-search');
const input = document.getElementById('search');
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
const query = input.value.trim();
if (query) element.open(query);
else element.open();
}
});
});open(query) opens the dialog and asks the question straight away. open() with no query opens an empty dialog.
Keep your search form's action and method in place. If the script ever fails to load, the form still goes to your classic search page.
4. Theming
The dialog renders in a Shadow DOM, which your site's stylesheet can't reach. Set your styles in the theme attribute, on .insytful-theme:
.insytful-theme {
--insytful-brand-primary: #da3949;
--insytful-font-family: 'Helvetica Neue', Arial, sans-serif;
}You can also write ordinary CSS rules in theme for anything a token doesn't cover. They're applied after the default styles, so you don't need !important.
Only put CSS your team has written in theme. Never build it from user input or CMS free-text fields, because it isn't sanitised.
See AI Search theming for the common tokens.
5. Attributes
| Attribute | Required | Purpose |
|---|---|---|
api-uri | Yes | Your AI Search API URL |
project-id | Yes | Your project ID |
theme | No | CSS for your branding (see above) |
suggestions-position | No | above (default) or below the input |
sections | No | Comma-separated section slugs to limit answers to |
dev-mode | No | Returns sample answers with no backend |
You can change any attribute after the page loads and the element updates itself. A change to api-uri, project-id, sections or dev-mode applies from the next question.
6. Slots
Slotted content stays part of your page, so your own stylesheet styles it as normal.
| Slot | What it's for |
|---|---|
trigger | The button that opens and closes the dialog |
title | Heading shown before the first question |
description | Text under the title |
disclaimer | Small print, such as an accuracy warning |
logo | Image shown before the first question |
avatar | Image shown next to each answer |
Trigger: you don't need a click handler. The element also keeps aria-expanded and data-state="open" | "closed" up to date on your button, so you can style it:
[data-insytful-toggle][data-state="open"] { /* … */ }
Avatar: the element copies your avatar next to each answer, so your page CSS doesn't reach those copies. Style them with .insytful-search-message-logo in theme. Changing the original after the page loads has no effect.
7. Child elements
These configure the dialog. They're read once when the page loads and don't appear on the page themselves.
<!-- Suggestions shown before the first question -->
<insytful-suggestion>How do I apply?</insytful-suggestion>
<!-- Close button. Leave empty for the default ✕, or add your own content -->
<insytful-close></insytful-close>
<insytful-close aria-label="Close search">Cancel</insytful-close>
<!-- Modes: AI answers plus your classic search results -->
<insytful-mode name="ai">AI Search</insytful-mode>
<insytful-mode name="classic" path="/search?q=">Classic Search</insytful-mode>
<!-- Your own error message (optional; defaults are used without it) -->
<insytful-callout type="error">
<insytful-callout-title>We couldn't get an answer</insytful-callout-title>
<insytful-callout-text>Please try again, or contact us if the problem continues.</insytful-callout-text>
<insytful-callout-cta href="/contact">Contact us</insytful-callout-cta>
</insytful-callout>Classic mode: the visitor's question is added to the end of path, so path must end with the query parameter and =. Only links to your own site are followed.
8. JavaScript API
const element = document.querySelector('insytful-search');
element.open(); // open the empty dialog
element.open('query'); // open and ask straight away
element.close();
element.toggle();
element.isOpen; // true or false; can also be set
element.searchingText = 'Looking that up…'; // loading message
element.renderMarkdown = (md) => myParser(md); // optional; must return an HTML stringAnswers are formatted from markdown automatically. Only set renderMarkdown if you want a different parser. Its output is still sanitised.
Events
All events bubble, so you can listen on the element or anywhere above it.
| Event | e.detail |
|---|---|
insytful-open | none |
insytful-close | none |
insytful-search | { query }: a question was asked |
insytful-message | { role, content, ctas? }: an answer finished |
insytful-mode-change | { mode }: the visitor switched tab |
insytful-error | { error }: the error message |
insytful-cta-click | The quick-action button that was clicked |
element.addEventListener('insytful-search', (e) => {
analytics.track('ai_search', { query: e.detail.query });
});9. Quick-action buttons
Answers can come with quick-action buttons (call, email, link or a custom event), which are set up in the CMS. Call, email and link buttons work without any setup.
An event button sends a named event that you listen for on the page. Use it to open a web chat or a booking form, for example:
<script>
(window.insytfulAISearchEvents ??= new EventTarget())
.addEventListener('openWebChat', (e) => {
// e.detail has the button's configured data
});
</script>Write it this way so it works whether your script loads before or after the search script. To track every click, listen for insytful-cta-click.
10. Sticky headers
If your site has a sticky header, mark it so the dialog opens below it:
<header data-insytful-offset>Your sticky header</header>You can mark more than one element, such as a cookie banner and a header, and their heights are added together. The dialog follows any height changes automatically. Only mark elements that stay on screen when the page scrolls.
11. Test without a backend
Add dev-mode while your API is being set up:
<insytful-search api-uri="…" project-id="…" dev-mode>The search returns a sample answer that uses every markdown feature and includes one quick-action button of each type, which is useful for testing your styles. It waits 8 seconds before the first words appear so you can see the loading state.
Remove dev-mode before going live.
Full reference
For every hook class, state attribute and CSS token, see the full reference on npm.