---
title: "Widget embed"
description: "The <canary-lytics> web component embeds live CanaryLytics stats in any page on a trusted domain. It runs on the read-only key and is themeable with CSS custom properties."
last_updated: "2026-07-30"
---

# Widget embed

`<canary-lytics>` is a self-contained custom element (Shadow DOM, Preact-rendered, shipped as a single-file IIFE bundle) that renders live stats for your project inside any page you control. Drop it into any page whose origin is one of the project's trusted domains:

```html
<script src="https://cdn.analytics.canarycoders.es/widget.js" defer></script>
<canary-lytics
  api-key="cly_read_a1b2c3d4e5f60718293a4b5c6d7e8f90"
  window="7d"
  panels="stats,chart,sources,countries,live"
  theme="auto"
  refresh="10"
></canary-lytics>
```

<Aside type="danger" title="Use the read-only key">
  Anyone who views source on the host page can read the `api-key` attribute. Always use a `cly_read_…` key here. It can only read stats, never rotate keys, change settings or write events. See [Keys & security](/docs/keys/).
</Aside>

## Attributes

| Attribute | Default | Description |
|---|---|---|
| `api-key` | required | A `cly_read_…` key. Without it the widget renders an inline error instead of data. |
| `base-url` | `https://api.analytics.canarycoders.es` | API origin override (dev/self-host). |
| `window` | `7d` | One of `24h`, `7d`, `30d`, `90d`. |
| `panels` | `stats,chart,sources,countries` | Comma-separated subset of `stats`, `chart`, `sources`, `countries`, `pages`, `events`, `live`, in the order you want them rendered. Unknown values are dropped; an empty or invalid list falls back to the default. |
| `theme` | `auto` | `light`, `dark`, or `auto` (follows `prefers-color-scheme` and updates live if it changes). |
| `refresh` | `10` | Poll interval in seconds. `0` disables polling (fetch once). |

Attributes are reactive: changing any of them on the element re-renders the widget without a page reload.

## Why it needs a trusted origin

The widget calls the same `/v1` REST endpoints as the SDK, from the browser, using the `api-key` attribute as its Bearer token. `/v1`'s CORS layer only reflects `Access-Control-Allow-Origin` for the authenticated project's trusted domains (or a dashboard/localhost-with-toggle origin), so embedding the widget on an untrusted page fails the same way any other cross-origin fetch would, even with a valid key.

## Theming

Everything is scoped under a `.cly` class inside the shadow root, so nothing leaks in from the host page's styles, or out to them. Look and feel is controlled entirely through CSS custom properties, which **do** cross the shadow boundary (they are inherited properties). Set them on the `canary-lytics` element itself, or any ancestor, from the host page's own stylesheet:

```css
canary-lytics {
  --cly-accent: #7c3aed;
  --cly-border: #d4d4d8;
}
```

| Property | Light default | Dark default | Used for |
|---|---|---|---|
| `--cly-bg` | `#ffffff` | `#101013` | Widget background |
| `--cly-card` | `#f8f8f8` | `#18181b` | Panel/card background |
| `--cly-fg` | `#18181b` | `#f4f4f5` | Primary text |
| `--cly-muted` | `#6b7280` | `#9ca3af` | Secondary/label text |
| `--cly-border` | `#e4e4e7` | `#27272a` | Card and container borders |
| `--cly-accent` | `#b45309` | `#fb7185` | Flash-on-increment stat color |
| `--cly-accent-soft` | `rgba(244,63,94,.18)` | `rgba(244,63,94,.22)` | Flash-on-increment background |
| `--cly-bar` | `rgba(244,63,94,.22)` | `rgba(244,63,94,.26)` | Ranked-list row bars (sources/countries/pages/events) |
| `--cly-line` | `#e11d48` | `#e11d48` | Sparkline line color |
| `--cly-area` | `rgba(244,63,94,.14)` | `rgba(244,63,94,.18)` | Sparkline fill |
| `--cly-live` | `#16a34a` | `#22c55e` | Live-visitors pulse dot |
| `--cly-live-glow` | `rgba(22,163,74,.45)` | `rgba(34,197,94,.45)` | Live-visitors pulse glow |
| `--cly-danger` | `#dc2626` | `#f87171` | Error text |

Panel-specific notes: `stats` renders session/pageview/bounce tiles that briefly flash `--cly-accent-soft` on increment; `chart` is an SVG sparkline over the timeseries; `live` replaces the header's window label with a pulsing current-visitors badge instead of occupying a panel slot.

## Related

<CardGrid>
  <LinkCard title="Keys & security" description="Why the read-only key is safe to embed." href="/docs/keys/" />
  <LinkCard title="REST API" description="The /v1 endpoints the widget calls under the hood." href="/docs/api/rest/" />
</CardGrid>

## Sitemap

- [Docs sitemap](https://analytics.canarycoders.es/docs/sitemap.md)
- [Full documentation as a single file](https://analytics.canarycoders.es/llms-full.txt)
