Script tag
The SDK's client entry is a self-contained ES module, so any server-rendered application can load it from jsDelivr without npm or a bundler. Let's add the chat sidebar with a script tag, send the token request through your framework's CSRF protection, and wire it into a Ruby on Rails app.
Load the widget
Let's give the widget a container with a definite height and mount it from a module script:
<div id="astralbeam-chat" style="height: 100vh"></div>
<script type="module">
import { mountAstralBeamChat } from "https://cdn.jsdelivr.net/npm/@astralbeam/sdk@0.12.2/dist/client.js"
mountAstralBeamChat(document.getElementById("astralbeam-chat"), {
title: "Acme Assistant",
fetchAstralBeamToken: { url: "/astralbeam/token" },
})
</script>- Use
type="module". The 3 KB loader imports the chat chunk lazily, relative to its own URL. - Import the full
/dist/client.jspath. The bare package URL has no browser entry. - Pin an exact version. A range such as
@0lets jsDelivr's cache serve the loader and its chunks from different releases, which fails with a 404 after each release. - Every option in Configuration works the same, and the returned handle has the same
update,reset,stop, andunmountmethods. mountAstralBeamTenantUserListandmountAstralBeamTenantListload from the same URL. See Tenant directories.
NOTE: If your application sends a Content Security Policy, allow https://cdn.jsdelivr.net in script-src and your AstralBeam API in connect-src. The widget injects its stylesheet into its shadow root with a <style> element, so style-src must also allow inline styles.
Add the token endpoint
The widget calls your server for a short-lived token before it chats. Your backend signs that token with your API key, in any language with a JWT library. Authentication describes the endpoint's rules and the token format, with a Ruby example.
Frameworks with CSRF protection reject the widget's POST without a CSRF token. Let's send it as a standard RequestInit header, here read from the <meta name="csrf-token"> tag that Rails and Laravel render:
const csrfToken = document.querySelector("meta[name=csrf-token]").content
mountAstralBeamChat(element, {
fetchAstralBeamToken: { url: "/astralbeam/token", headers: { "X-CSRF-Token": csrfToken } },
})Ruby on Rails
Rails loads JavaScript through import maps, so the SDK needs no npm or bundler there either. Let's wire the widget into a Rails 8 app:
Pin the SDK by URL in
config/importmap.rb, next to a pin for the page's own module. Avoidbin/importmap pin, because it vendors one file and the loader's lazy chunks would be missing.pin "assistant" pin "@astralbeam/sdk/client", to: "https://cdn.jsdelivr.net/npm/@astralbeam/sdk@0.12.2/dist/client.js"Mount the widget from that module in
app/javascript/assistant.js:import { mountAstralBeamChat } from "@astralbeam/sdk/client" const csrfToken = document.querySelector("meta[name=csrf-token]").content mountAstralBeamChat(document.getElementById("astralbeam-chat"), { fetchAstralBeamToken: { url: "/astralbeam/token", headers: { "X-CSRF-Token": csrfToken } }, })Render the mount point in a view and load the module with
javascript_importmap_tags "assistant"in the layout's<head>.<div id="astralbeam-chat" style="height: 100vh"></div>Add
post "astralbeam/token" => "astral_beam_tokens#create"toconfig/routes.rband a controller that mints the token as shown in Authentication.
- A full page load discards the transcript. Tools and forms that change server data should call your controllers with
fetchand update the page in place. - Widgets render into the mount point's light DOM, so your stylesheet styles them. Name your CSS custom properties distinctly, because the widget's own tokens such as
--cardand--bordershadow same-named variables inside the conversation.
TIP: The examples/todos-rails app is a complete Rails 8 integration with host tools over a JSON API, a todoCard widget, and the tenant-user directory.