EvoHub DocsEvoHub Docs Sign in

Embed the What's new widget

Use with AI
View as MarkdownThis page as plain text, for pasting into an AI tool Open in ClaudeAsk Claude questions about this page Open in ChatGPTAsk ChatGPT questions about this page
Connect to Cursor / VS Code / ClaudeSearch and read these docs from your AI tool (MCP server)

MCP server URL

https://docs.evohub.io/mcp

Claude Code

claude mcp add --transport http evohub-docs https://docs.evohub.io/mcp

Claude (claude.ai and Claude Desktop): Settings → Connectors → Add custom connector, and paste the URL above.

Claude Desktop — claude_desktop_config.json

{
  "mcpServers": {
    "evohub-docs": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://docs.evohub.io/mcp"
      ]
    }
  }
}

Cursor — ~/.cursor/mcp.json

{
  "mcpServers": {
    "evohub-docs": {
      "url": "https://docs.evohub.io/mcp"
    }
  }
}

VS Code — .vscode/mcp.json

{
  "servers": {
    "evohub-docs": {
      "type": "http",
      "url": "https://docs.evohub.io/mcp"
    }
  }
}

The "What's new" widget puts your changelog inside your own website or app: a button that opens a panel with the 10 newest entries, and a red badge with the number of entries the visitor has not seen yet. This page shows how to embed it in plain HTML and in a React app, and how to drive it from your own code.

Before you start

  • The changelog is published and has an active custom domain. The widget loads from that domain, so the console shows no snippet until one is connected. See Changelog settings.
  • In Settings, Widget, Show the widget is on. When it is off, the widget shows nothing.
  • The changelog is public. On a changelog with password access, the widget is off.

The widget's label comes from Button label in the same tab (1 to 30 characters; "What's new" by default), and its colour from the changelog's brand colour.

Add the script

Copy the snippet

In Settings, Widget, under Add it to your site, copy the Script snippet. It has your changelog's domain and slug filled in:

<script src="https://changelog.example.com/widget.js" data-site="acme" async></script>

Paste it into your pages

Add the tag to every page where the widget should appear, before the closing </body> tag. The script is small, has no dependencies and loads asynchronously.

Check it

Open your page. A button appears in the corner you chose under Position. Click it to open the panel.

data-site must be the changelog's slug, and the script must be loaded from the changelog's own domain. If the slug does not match that changelog, the widget does not appear and the browser console says why.

Attributes

Attribute What it does
data-site Required. The changelog's slug.
data-position bottom-right, bottom-left or none. Overrides Position in the settings. With none, no floating button is drawn: you open the panel yourself (see below).
data-selector A CSS selector for your own button or link, for example #whats-new. See Use your own button.
data-theme light, dark or auto (the default, which follows the visitor's system setting).

Use your own button

To open the widget from a link or button in your own navigation instead of a floating button, point data-selector at it:

<button id="whats-new" type="button">What's new</button>

<script src="https://changelog.example.com/widget.js" data-site="acme" data-selector="#whats-new" async></script>

With a selector, the widget:

  • draws no floating button,
  • puts the unread badge on the top-right corner of every element the selector matches,
  • opens the panel below that element when it is clicked (on narrow screens, the panel opens from the bottom of the screen instead).

The elements must already be on the page when the script has loaded; the widget looks for them once.

React example

In a single-page app, load the script once and keep the element it attaches to on the page — for example in your app's header:

import { useEffect } from "react";

const CHANGELOG = "https://changelog.example.com";
const SITE = "acme";

export function WhatsNewButton() {
  useEffect(() => {
    // The widget runs once per page; a second copy of the script does nothing.
    if (document.querySelector(`script[src="${CHANGELOG}/widget.js"]`)) return;
    const script = document.createElement("script");
    script.src = `${CHANGELOG}/widget.js`;
    script.async = true;
    script.dataset.site = SITE;
    script.dataset.selector = "#whats-new";
    document.body.appendChild(script);
  }, []);

  return (
    <button id="whats-new" type="button">
      What's new
    </button>
  );
}

Render WhatsNewButton in a part of the layout that stays mounted, such as the header. If the button is removed from the page and drawn again later, the badge and the click handler do not come back until the page is reloaded; in that case use the JavaScript API below instead.

The JavaScript API

Once the script has loaded, it adds window.EvoHubChangelog:

Call What it does
EvoHubChangelog.open() Opens the panel.
EvoHubChangelog.close() Closes it.
EvoHubChangelog.unread() Returns how many entries the visitor has not seen.

When it has loaded, and each time the count changes, the widget fires an evohub-changelog:unread event on document, with the count in event.detail.count. Use it to draw a badge of your own.

A click anywhere outside the panel closes it, and that includes the click that called open(). Stop that click from bubbling up to the document, or the panel closes as soon as it opens:

<button id="news" type="button">What's new <span id="news-count"></span></button>

<script src="https://changelog.example.com/widget.js" data-site="acme" data-position="none" async></script>
<script>
  document.getElementById("news").addEventListener("click", function (event) {
    event.stopPropagation();
    if (window.EvoHubChangelog) window.EvoHubChangelog.open();
  });
  document.addEventListener("evohub-changelog:unread", function (event) {
    document.getElementById("news-count").textContent = event.detail.count || "";
  });
</script>

In React, call event.stopPropagation() in the onClick handler in the same way.

What the visitor sees

  • The panel lists the 10 newest entries, pinned entries first, each with its date, version, categories, title and summary. A title opens the entry on your changelog in a new tab.
  • The footer links to the whole changelog and, when email subscriptions are on, to Subscribe.
  • Entries the visitor has not seen are highlighted and counted in the badge; above 9 it shows "9+". On a visitor's first visit, entries from the last 30 days count as new. Opening the panel marks everything in it as seen.
  • The panel closes with Esc, the close button or a click outside it, and keyboard focus stays inside it while it is open.
  • The widget's text follows the changelog's Language setting.

What a visitor has seen is kept in their browser's local storage, so it is per browser, not per person. The widget sets no cookies. It keeps the latest entries in local storage for a minute, so page views within a minute do not fetch them again. When the changelog counts its readers, widget opens and clicks are counted too, except for visitors who send Do Not Track or Global Privacy Control.

Embed the list in a page

Where you cannot add scripts, or to show the list inside a page rather than in a panel, use the iframe from the same Add it to your site card:

<iframe src="https://changelog.example.com/embed" title="What's new" loading="lazy" style="border:0;width:100%;height:480px"></iframe>

Under Allowed sites in Settings, Widget, list the origins allowed to show the iframe, such as https://app.example.com (up to 20; http://localhost is accepted for testing). Leave it empty to allow any site. Allowed sites applies only to the iframe; the script works on any site.

Content Security Policy

If your site sets a Content Security Policy, allow your changelog's domain:

script-src https://changelog.example.com; connect-src https://changelog.example.com; style-src https://changelog.example.com; frame-src https://changelog.example.com

script-src and connect-src are needed for the script. style-src is only needed for older browsers that cannot style the widget from the script, and frame-src only for the iframe.

Troubleshooting

  • Nothing appears: check that the changelog is published and public, that Show the widget is on, and that the script's address is the changelog's domain and data-site is its slug. The browser console shows a warning starting with [EvoHub Changelog] when the slug does not match.
  • The panel opens and closes at once: your click handler calls open() without stopping the click; see The JavaScript API.
  • No badge on your own button: the element the selector names was not on the page when the script loaded, or it was drawn again since.
  • A new entry does not show yet: the widget's list can be up to a couple of minutes old in the browser and at the edge.
Last updated Powered by