Skip to main content

AI Search Classic Contensis Implementation

Log in to add to favourites

Page 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

HTML
<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

HTML
@{
    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.
  • id is 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:

JavaScript
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:

SCSS
.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

AttributeRequiredPurpose
api-uriYesYour AI Search API URL
project-idYesYour project ID
themeNoCSS for your branding (see above)
suggestions-positionNoabove (default) or below the input
sectionsNoComma-separated section slugs to limit answers to
dev-modeNoReturns 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.

SlotWhat it's for
triggerThe button that opens and closes the dialog
titleHeading shown before the first question
descriptionText under the title
disclaimerSmall print, such as an accuracy warning
logoImage shown before the first question
avatarImage 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.

HTML
<!-- 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

JavaScript
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 string

Answers 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.

Evente.detail
insytful-opennone
insytful-closenone
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-clickThe quick-action button that was clicked
JavaScript
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:

JavaScript
<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:

HTML
<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:

HTML
<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.

Still need help?

If you still need help after reading this article, don't hesitate to reach out to the Insytful community on Slack or raise a support ticket to get help from our team.
New support request