Jamdesk Documentation logo

Embed a Page

Add a "What's new?" button to your own app that opens your Jamdesk changelog in a modal, with an unread dot. One script tag, no build step.

Your changelog already lives in your docs. This guide puts a "What's new?" trigger inside your own product: a button or floating launcher that opens those same entries in a modal, with a dot that marks unread updates per visitor. You paste one <script> tag; Jamdesk hosts and versions the widget.

The changelog is the common case, and the one the unread dot is built around. The same widget can open any docs page in the modal, though — point data-page wherever a focused, in-context page helps (see Point the modal at any page).

The What's new modal open over a dimmed app, showing the Jamdesk changelog page with a Copy page button and dated update entries, and a close button in the top corner

Prerequisites

  • A published Jamdesk site on its *.jamdesk.app subdomain (the widget always loads from there, even if you also serve docs on a custom domain).
  • A changelog page built from <Update> entries with rss: true in its frontmatter (see Opt in with rss: true).

Quick Start

Open your dashboard, go to Settings → What's New widget, set the page and launcher options, and copy the generated snippet. It looks like this:

<script
  src="https://acme.jamdesk.app/_jd/widget.js"
  data-base="https://acme.jamdesk.app"
  data-page="/changelog"
  data-theme="auto"
  async
></script>

Paste it into your app's HTML, before the closing </body> tag. On load it adds a floating What's new launcher in the corner. Clicking it opens your changelog in a modal; an unread dot appears when there's an entry the visitor hasn't seen.

Replace acme with your own subdomain. The dashboard card fills this in for you and keeps the data-base value pointed at the right origin, including the /docs path if you host docs under a subpath.

Pin a Version or Self-Host

The widget is open source, and the hosted snippet above always serves the latest version — the right default for most sites. When you'd rather freeze a known version or serve the file yourself, the jamdesk-widget repository offers two more ways to load it.

Pin a version with jsDelivr. Load a tagged release from the CDN and the bytes never change under you:

<script
  src="https://cdn.jsdelivr.net/gh/jamdesk/jamdesk-widget@v1.0.0/widget.js"
  data-base="https://acme.jamdesk.app"
  data-page="/changelog"
  async
></script>

Self-host. Download widget.js from the latest release and serve it from your own origin. This helps when a strict script-src policy rules out third-party scripts.

Either way, set data-base to your *.jamdesk.app origin: the hosted snippet reads it from the script's own URL, but a CDN or your own server can't. Each release publishes a Subresource Integrity hash so you can pin the exact bytes. The repository README walks through all three install paths.

Opt in with rss: true

The widget reads the newest entry from the same feed that powers your RSS, so a page only feeds the widget when its frontmatter sets rss: true:

---
title: Changelog
rss: true
---

<Update label="June 2026" date="2026-06-01">

**Spec validation at build time.** Every deploy validates the OpenAPI specs your `docs.json` references.

</Update>

Without rss: true, the widget loads but shows no entries and the launcher stays hidden. A docs page that merely demonstrates the <Update> component (with no rss: true) is correctly left out, so a demo date never lights up the dot.

Each <Update> that feeds the widget needs a date (any value Date.parse reads, like 2026-06-01), not just rss: true on the page. The date is what orders the feed and identifies the newest entry, so entries without one are skipped. If none of your entries are dated, the floating launcher stays hidden and the unread dot never appears — even with rss: true set.

Configure the Snippet

Every option is a data- attribute on the script tag. The dashboard card writes these for you, but you can also hand-edit the snippet.

AttributeValuesDefaultPurpose
data-baseYour site URLscript originThe *.jamdesk.app origin (plus /docs if you host under a subpath).
data-pagePath/changelogAny docs path to open in the modal, not just the changelog. See Point the modal at any page.
data-themeauto, light, darkautoForce the modal's color scheme, or follow the visitor's system setting.
data-positionbottom-right, bottom-left, top-right, top-leftbottom-rightWhich corner the floating launcher sits in. Ignored when data-trigger is set.
data-labelTextWhat's newThe floating launcher's button text.
data-widthCSS length560pxModal width. See Size the modal.
data-heightCSS length680pxModal height.
data-radiusCSS length12pxThe modal's corner radius. Lower it for squarer corners.
data-unreadoff to disableonWhether to show the unread dot.
data-unread-colorHex or CSS color name#e5484dThe unread dot's color.
data-button-colorHex or CSS color name#111Floating launcher background. Ignored when data-trigger is set.
data-button-text-colorHex or CSS color name#fffFloating launcher text color.
data-triggerCSS selector(none)Bind to your own element instead of the floating launcher.
data-projectSlugderived from data-baseThe key the per-visitor "seen" state is stored under. Override only if one origin serves more than one changelog.

Point the modal at any page

data-page opens any path on your docs site, not only /changelog. The modal renders whatever page you name, stripped of its site chrome — a single announcement, a getting-started guide, a migration note. Point it wherever a focused, in-context page helps:

<script
  src="https://acme.jamdesk.app/_jd/widget.js"
  data-base="https://acme.jamdesk.app"
  data-page="/announcements/2026-migration"
  data-unread="off"
  async
></script>

Two behaviors stay tied to your changelog feed, so keep them in mind when the page isn't a changelog:

  • The unread dot tracks your changelog's newest entry, not the page in the modal. Set data-unread="off" when the modal opens something else, or the dot lights up for changelog updates the visitor never sees there.
  • The floating launcher only auto-appears once your changelog has an entry (see Opt in with rss: true). To embed a page on a site with no changelog, bind to your own element with data-trigger — your element shows regardless.

Launcher modes

Floating launcher

Leave data-trigger off and the widget renders its own button in the corner set by data-position, with the text from data-label.

Bind to your own element

Set data-trigger="#whats-new" (any CSS selector) and the widget opens from your existing nav link or button instead.

When you bind to your own element, the widget adds the unread dot to that element and never renders a floating button (so data-position and data-label no longer apply):

<script
  src="https://acme.jamdesk.app/_jd/widget.js"
  data-base="https://acme.jamdesk.app"
  data-page="/changelog"
  data-trigger="#whats-new"
  async
></script>

Size the modal

The modal opens at 560 × 680 px. Set data-width and data-height to change it. A bare number reads as pixels, or use any px, vw, vh, rem, em, or % value:

<script
  src="https://acme.jamdesk.app/_jd/widget.js"
  data-base="https://acme.jamdesk.app"
  data-page="/changelog"
  data-width="720px"
  data-height="600px"
  async
></script>

Both dimensions are capped responsively (92vw wide, 86vh tall), so a large size still fits on a phone. An unrecognized value falls back to the default. The corners default to a 12px radius; set data-radius (any CSS length) to square them off or round them further.

Style the launcher button

The floating launcher is a dark pill by default. Recolor it with data-button-color (background) and data-button-text-color (text), each a hex value or a CSS color name:

<script
  src="https://acme.jamdesk.app/_jd/widget.js"
  data-base="https://acme.jamdesk.app"
  data-page="/changelog"
  data-button-color="#4f46e5"
  data-button-text-color="#ffffff"
  async
></script>

These two attributes only style the widget's own floating button. When you bind to your own element with data-trigger, the launcher inherits that element's styling, so they have no effect.

Customize the unread dot

The unread dot is red (#e5484d) and on by default. Recolor it with data-unread-color (a hex value or a CSS color name), or turn it off with data-unread="off":

<!-- Recolor the dot -->
<script src="https://acme.jamdesk.app/_jd/widget.js" data-base="https://acme.jamdesk.app" data-unread-color="#7c3aed" async></script>

<!-- Turn the dot off -->
<script src="https://acme.jamdesk.app/_jd/widget.js" data-base="https://acme.jamdesk.app" data-unread="off" async></script>

Turning the dot off keeps the launcher and modal — it only removes the indicator. The next section explains how "seen" state is tracked.

The Unread Dot

The widget keeps an unread indicator per visitor. It compares the newest entry's id against a value in the browser's localStorage (one key per project). When they differ, a dot shows on the launcher; opening the modal marks the entry seen and clears the dot until the next update ships.

Because the state lives in localStorage, it's per-browser and per-visitor — there's no account or tracking, and clearing site data resets it. A visitor in a fresh browser sees the dot once, then not again until you publish something new.

Examples

Floating, bottom-left, green dot
<script
  src="https://acme.jamdesk.app/_jd/widget.js"
  data-base="https://acme.jamdesk.app"
  data-page="/changelog"
  data-position="bottom-left"
  data-unread-color="#22c55e"
  async
></script>
Bound to a nav link, larger modal
<script
  src="https://acme.jamdesk.app/_jd/widget.js"
  data-base="https://acme.jamdesk.app"
  data-page="/changelog"
  data-trigger="#whats-new"
  data-width="720px"
  data-height="600px"
  async
></script>
No dot, custom label
<script
  src="https://acme.jamdesk.app/_jd/widget.js"
  data-base="https://acme.jamdesk.app"
  data-page="/changelog"
  data-label="Release notes"
  data-unread="off"
  async
></script>

Content-Security-Policy

If your own site sends a strict Content-Security-Policy, allow your *.jamdesk.app origin in three directives, or the widget silently does nothing:

Content-Security-Policy:
  script-src  https://acme.jamdesk.app;
  frame-src   https://acme.jamdesk.app;
  connect-src https://acme.jamdesk.app;
  • script-src loads widget.js.
  • frame-src renders the modal's iframe.
  • connect-src fetches the changelog metadata for the unread dot.

Miss one and there's no error banner — the launcher just won't appear or the modal stays blank. Check your browser console for CSP violations if the widget doesn't show.

Password-Protected Sites

Don't embed the widget if your docs site is password-protected. The unlock screen is built to be entered first-party on your *.jamdesk.app site, not inside a third-party iframe. Embedding it would ask visitors to type your site password into a frame on another origin, which is exactly the shape of a phishing prompt. Keep the widget for public changelogs.

What's Next?

Update Component

Write the changelog entries the widget reads

Custom Domains

Serve docs on your own domain (the widget still loads from jamdesk.app)

Widget Source

Pin a version, self-host, or read the source on GitHub