Install guide

Installing a chat assistant in a Remix project

Remix has exactly one file that owns the document, and it is app/root.tsx. The install is a single line there. What is worth ten minutes of your attention is the Layout export, because it decides whether the document your visitors see during a failure still has your tag in it.

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 Remix

app/root.tsx, inside the Layout export, immediately before the closing body tag and after the Scripts component.

Every Remix application, with nothing to enable. The root route is required by the framework, so the file you need is already in your project and already committed.

Step by step

  1. 1Open app/root.tsx. This is the only route Remix requires, and it is the one that returns the html and body elements.
  2. 2If the file has a Layout export, work in that. It receives a children prop that may be your app, your ErrorBoundary or your HydrateFallback, which is exactly why it is the right home for anything that must be on every document.
  3. 3Find the closing body tag. Above it you will see ScrollRestoration and Scripts.
  4. 4Paste the tag on its own line after Scripts and before the closing body tag. Keep src, defer and data-key exactly as issued.
  5. 5If your project has no Layout export and instead repeats the shell in the default component and again in ErrorBoundary, add the export now and move the shell into it once, rather than pasting the tag twice.
  6. 6Start the development server, open a page, and confirm the launcher appears. Then click through two or three routes and confirm it stays.

What goes wrong on Remix

An error boundary can display a document with no tag in it

When a loader or a component throws, Remix stops using your default root component and uses ErrorBoundary instead. In projects that predate the Layout export, ErrorBoundary carries its own copy of the html and body elements, and that copy is almost always a stripped down one somebody wrote in a hurry. Your tag is not in it.

The symptom is a widget that is present everywhere except on the pages where a visitor most needs to ask a question. Putting the shell in the Layout export solves it permanently, because Layout wraps the error branch and the success branch alike.

A Layout export that throws takes the whole document with it

Layout is used while displaying errors, so anything inside it has to be safe during a failure. Calling the loader data hook directly in Layout is the common way people break this: on a route where the root loader threw, that hook has nothing to hand back, Layout throws in turn, and you get a bare fallback document with no styling, no scripts and no launcher.

Read root loader data with the route loader data hook and guard for undefined before using it. Keep the tag itself dependency free, which it is, since it is plain markup with three static attributes.

No route export emits a script element

Remix gives routes a links export and a meta export, and neither one can produce a script tag: links emits link elements, meta emits meta and title. People look for a scripts export by analogy, do not find one, and conclude the framework is blocking them.

It is not. Markup in the root document is the supported route and the only one you need. If you genuinely want the widget on some routes and not others, keep the tag in root and call the destroy method from the routes that should not have it.

Origins to allowlist

These are the origins a Remix site is typically served from. List every one you want the assistant to answer on, including the ones only you visit.

  • http://localhost:3000
  • http://localhost:5173
  • 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.

Loader behaviour
One tag, nothing elseThere is no second file, no stylesheet and no package to install. The tag is the whole install.
Where it goesImmediately 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 requiredWithout it the loader stops and writes a warning to the browser console rather than rendering anything.
It finds itselfThe 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 onceA 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 tagEverything 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 lateOnly 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 collideThe 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 line from app/root.tsx and redeploy. If the shell is duplicated across the default component and ErrorBoundary, check both before you assume it is gone.

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

Does client side navigation reinstall it on every route change?
No. Remix swaps route content underneath the same document, so the loader runs once on the first load and the launcher stays mounted. A conversation opened on one page is still open after the visitor moves to another.
Should it go above or below the Scripts component?
Below, as the last thing before the closing body tag. It makes no functional difference because the tag defers, but keeping the framework's own scripts together and third party markup after them makes the file easier to read a year from now.
Will it cause a hydration warning?
No. The markup you write is the markup the server sends and the markup the browser hydrates, so the two agree. Warnings come from content that differs between the two, which a static tag with fixed attributes never does.
What about a route that is served outside Remix entirely?
A path handled by your server before Remix sees it, such as a status endpoint or a legacy page, has its own HTML and does not inherit root. Add the tag there separately if that page needs it.

Keep reading

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.