Install guide
Installing a chat assistant on VitePress documentation
VitePress puts every head tag in one array in one configuration file, which makes this a two minute job. The one thing to get right is the shape of the entry: it is a tag name followed by an object of attributes, and everything the loader needs lives inside that object.
The tag
<script src="https://cdn.askably.xyz/w.js" data-key="pk_live_YOUR_KEY" defer></script>Your own key replaces the placeholder. You will find it on the install tab of the assistant.
The origin the page is served from has to be on the allowlist for that assistant, or nothing renders and the browser console says which origin was refused. An apex domain and its www are two different origins to a browser, so list both, along with any staging or preview host you want it to work on.
Where it goes on VitePress
The head array in .vitepress/config.ts, or the .js, .mjs or .mts file of the same name, at the top level of the exported configuration.
Any VitePress site you build yourself. The head array is part of the core site configuration, so there is no plugin to install and no theme component to replace.
Step by step
- 1Open .vitepress/config.ts and find the head array at the top level of the exported config. Add the array if the file does not have one yet.
- 2Add one entry. An entry is an array whose first item is the tag name as a string and whose second item is an object of attributes.
- 3Use script as the tag name, then put src, defer and the data key into the attributes object.
- 4Give defer an empty string rather than a boolean. The attributes object is typed as strings, and an empty string prints as a bare attribute on the tag, which is what a deferred script needs.
- 5Save. The development server restarts itself when the configuration changes, so reload the browser once and check the page source for the tag.
- 6Build and deploy, then confirm the launcher appears on a deep page and not only on the home page.
What goes wrong on VitePress
A boolean in the attributes object is not what the type expects
The attributes object maps attribute names to string values. Writing defer as a boolean true is the natural thing to type and it is the wrong shape, so a project with type checking turned on stops at the build and a project without it can end up with an attribute value of true rather than a bare defer.
An empty string is the correct value. It prints as defer with nothing after it, which is exactly how a deferred script is written by hand.
The same tag added twice, once in config and once in frontmatter
VitePress lets an individual page add head tags through its own frontmatter, on top of whatever the site configuration contributes. A team that tried the frontmatter route first and then moved to the configuration file usually forgets to take the frontmatter entry out.
You will not see two launchers, because the loader checks for its own global and returns without doing anything the second time. You will see a second request for the same file on that one page, which is the only visible trace of the duplicate and the reason it goes unnoticed for months.
Head configuration reaches the not found page as well
The head array is applied to every generated page, and that includes the 404 page VitePress builds for you. This is usually what you want, since somebody who landed on a dead documentation link is a good candidate for a question.
It also means a mistake in the allowlist announces itself there. If your production host serves the not found page from a different address than the docs themselves, that address needs allowlisting too or the console will name it as refused.
Origins to allowlist
These are the origins a VitePress site is typically served from. List every one you want the assistant to answer on, including the ones only you visit.
- http://localhost:5173
- https://docs.yourdomain.com
- https://yourdomain.com
How the tag behaves, wherever you put it
The rest of this is the same on every platform, so it is worth reading once rather than on each guide.
| One tag, nothing else | There is no second file, no stylesheet and no package to install. The tag is the whole install. |
|---|---|
| Where it goes | Immediately before the closing body tag. It works in the head too, because it defers, but the body keeps it out of the way of anything that measures render order. |
| data-key is required | Without it the loader stops and writes a warning to the browser console rather than rendering anything. |
| It finds itself | The loader reads the tag it was loaded from, and falls back to the first tag on the page carrying a data-key. Platforms that move or re-inject the tag do not break it. |
| It installs once | A second copy of the tag on the same page is ignored, so a template that renders twice does not produce two launchers. |
| Assets follow the tag | Everything else it needs is fetched from the same origin the tag was served from, so a proxy or a private domain needs no extra configuration. |
| The panel loads late | Only the small launcher is on the page at first. The conversation panel is fetched the first time a visitor opens it, in its own frame. |
| Styles cannot collide | The launcher renders inside a closed shadow root and the panel inside a frame, so nothing on the host page can restyle either, and neither can restyle the host page. |
Taking it off again
Delete the entry from the head array, and delete any frontmatter copy, then rebuild and deploy. The tag is baked into the generated HTML, so an old build keeps working until it is replaced.
Before you remove it
- Removing the tag stops the widget on that site. It does not delete the assistant, its material, or the enquiries it has already collected.
- If you are moving the widget to a different host rather than retiring it, add the new origin to the allowlist before you remove the old tag, or there is a window where neither works.
Questions
- The tag ends up in the head rather than before the closing body tag. Is that a problem?
- No. The tag defers, so the browser fetches it without blocking and runs it after the document has been parsed. Head or end of body gives the same behaviour, and VitePress only offers the head, which is fine.
- Does it survive navigation between documentation pages?
- Yes. After the first load VitePress routes in the browser, so the document is never replaced and the loader is never asked to install again. The launcher and any open conversation carry across every page the reader visits.
- Can I keep it out of the local development server?
- Yes. The configuration file is a module, so you can build the head array conditionally from an environment value and leave the entry out when you are working locally. Most teams do not bother and just allowlist the local port.
- What should a VitePress site feed the assistant?
- The built site, crawled from its sitemap. Your Markdown sources contain component syntax and directives that read badly out of context, so let the crawl read the published pages the way a reader does.
Keep reading
- Install on DocusaurusThe scripts array in docusaurus.config.js, at the top level of the exported configuration object.
- Install on MkDocsAn overrides directory named by custom_dir under the theme key in mkdocs.yml, holding a main.html that extends the theme's base template.
- Install on Zendesk GuideGuide admin, then Customize design, then Edit code on your live theme, then footer.hbs.
- Every install guideThe same one line of HTML, and where it goes on each platform.
Try it on your own material
Upload a document or point it at your site, paste one line of HTML, then ask it something only your business could answer.