USE CASE
Astro AI support chat widget
Add Deacon to an Astro site with one line in the layout every page uses. It answers visitors from your pages and says so when it doesn’t know.
Published
Deacon is an AI support chat that an Astro site picks up from one tag in its base layout. Visitors type a question and get an answer from your own pages in seconds. When your pages are silent on it, Deacon won’t invent something. It says it doesn’t know, puts an email box in front of the visitor, and emails you what they asked if they leave an address and you’re not already in the dashboard. Your reply goes back into their chat. The free plan covers 50 answers a month, without a card or an end date.
We read Astro’s documentation on 25 October 2026, with Astro 7.4 the current release. Agents can take this page in Markdown.
What to have ready
An account first, because the snippet carries your key. Open a Deacon account, and Install shows the snippet with a pk_ key in data-key. Then the address. Deacon only shows on domains listed on Install, and the one you signed up with is there, subdomains included, but a GitHub Pages or Netlify address the site also lives at needs adding. Astro needs no integration or package for this.
When Install sees an Astro build on your home page, it says so and offers your coding agent the job. Copy the prompt, give it to Claude Code, Cursor or Codex, and the agent adds the tag and checks it’s live.
The tag goes in your base layout
Most Astro sites wrap every page in one layout component, such as src/layouts/Layout.astro, and Astro’s layouts guide describes it as the page template holding <html>, <head> and <body>. Deacon’s tag belongs right before that layout’s </body>, with is:inline added. In the layout below, that <script> line is the only new one.
---
const { title } = Astro.props;
---
<html lang="en">
<head>
<meta charset="utf-8" />
<title>{title}</title>
</head>
<body>
<slot />
<script is:inline src="https://heydeacon.com/widget/v1/loader.js" data-key="pk_your_widget_key" async></script>
</body>
</html>
Pages that use a second layout need the tag there as well.
Why is:inline
Astro bundles a plain <script> by default. Its guide to scripts says it “will not process a <script> tag if it has any attribute other than src”, and that is:inline is how you load a script from “a remote server” untouched. Deacon’s tag already carries data-key and async, so Astro would leave it alone anyway. is:inline makes that explicit, and keeps it true if someone later trims the tag.
Testing on port 4321
- Start
npm run dev. Astro serves the site athttp://localhost:4321, and on that first visit the chat stays hidden, since localhost isn’t on the list. - Install picks it up, showing “Found the code on localhost:4321”. Add it in one click. Astro’s CLI reference says the preview server also defaults to 4321, so
npm run previewafter a build is covered too. - Reload and the button, called Help unless Appearance renames it, appears in the corner. Ask something your pages answer, for free.
- Click through to another page with the chat open. It should open again there, with the conversation in it.
Once you deploy, Install calls the domain Live and shows when Deacon was last seen. An agent can confirm the same without a browser. latest in the answer is the address that last loaded Deacon, and refused a development address it held back, as Deacon’s install guide explains.
curl -s "https://heydeacon.com/api/widget/seen?k=pk_your_widget_key"
Try Deacon for free
Install DeaconView transitions and the ClientRouter
A default Astro site loads every page in full. A chat that was open when the visitor clicked a link reopens on the next page, conversation and all.
With <ClientRouter />, Astro’s view transitions guide says “the <body> is completely replaced with the new page’s body” on each navigation, and an inline script in it may run again. Neither troubles Deacon. Its script mounts the chat on <html>, outside the body the router swaps, and once it’s running, a second run of the tag does nothing until the next full page load.
Starlight
Starlight generates its pages, so there’s no layout file to edit. Its configuration reference offers a head option for third-party scripts instead. That puts Deacon’s tag in the <head>, where it works just as well.
// astro.config.mjs
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
export default defineConfig({
integrations: [
starlight({
title: 'Your docs',
head: [
{
tag: 'script',
attrs: {
src: 'https://heydeacon.com/widget/v1/loader.js',
'data-key': 'pk_your_widget_key',
async: true,
},
},
],
}),
],
});
Content collections make good knowledge
An Astro site’s words often live in src/content as Markdown. Those files can go straight onto Deacon’s Knowledge Sources page, ten megabytes at most per file, which skips crawling altogether and doesn’t touch your monthly page allowance. MDX files need saving as .md first. A translated site that marks its languages with hreflang tags gets those folders left out when a paid plan crawls it, so Deacon learns your pages once rather than in every translation.
What visitors get, and what you learn
Visitors stop hunting through your pages. You start seeing what they hunted for. Deacon lists the questions it couldn’t answer, each with an Answer this button, and Topics counts how many people asked about each subject. The same script measures your visits, sources and goals on Analytics › Website.
Astro questions
Why is there no chat button on some pages?
Check the console on one of them for a [Deacon] line. “isn’t in your allowed domains” names an address to add on Install, such as a preview deploy, “Unknown widget key” means data-key isn’t yours, and “could not start” gives the error behind it. If there’s no [Deacon] line, those pages probably use a second layout without the tag, so add it there too. A chat that opens empty is a different problem. A console line about the chat frame that “did not load” means a Content Security Policy lacks https://heydeacon.com in frame-src.
What if people log in to my Astro site?
Pass Deacon the logged-in user’s id, email and name above the tag, from your server-rendered session, and window.deaconUser = null on a page where nobody is logged in, so the next person on that computer starts fresh. Their name then shows on the conversation, and they can get a reply without typing their email. Deacon’s install guide has the code, and Install’s “Do your users log in?” card confirms when it has seen one.
Try it on your own documentation
Add your help pages, ask it the question you know your docs cannot answer, and watch it say so.
The free plan covers 50 answers a month on one seat, and the paid plans are on the pricing page.
Free plan, no card.
READ NEXT
How to set up an AI chatbot for documentation
Which pages to let it learn from, what happens to your docs inside it, how to test it on questions customers really sent and how to keep it current.
How to add a chatbot to your website in 4 steps
How to add a chatbot to your website in four steps, where its script tag goes on each builder and framework, and how to tell it who’s logged in.
Adding an AI chatbot to a GitHub Pages site
Why a static GitHub Pages site needs a hosted chatbot, where its script tag goes on a Jekyll, plain HTML or Actions-built site, and what the options cost.