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
- 1Open app/root.tsx. This is the only route Remix requires, and it is the one that returns the html and body elements.
- 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.
- 3Find the closing body tag. Above it you will see ScrollRestoration and Scripts.
- 4Paste the tag on its own line after Scripts and before the closing body tag. Keep src, defer and data-key exactly as issued.
- 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.
- 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.
| 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 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
- Install on Next.jsapp/layout.tsx in the App Router, or pages/_document.tsx in the Pages Router. Not a client component that can unmount.
- Install on Reactindex.html at the project root for a Vite app, or public/index.html for Create React App. Immediately before the closing body tag.
- Install on Vueindex.html at the project root, after the div the app mounts into and immediately before the closing body tag.
- 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.