Request feature from anywhere with the one line embeddable widget.

Add the widget to any page in seconds - no complex setup required.
Embed your entire public board directly into your website to let users browse, vote, and comment without leaving your site.

Upvoted Embeddable Widget Integration Guide

This widget lets you inlcude the Upvoted feature requests in any website.

✨ Features

  • 🛡️ Fully Isolated - Shadow DOM encapsulation prevents style conflicts
  • 🚀 Modern Stack - Built with React 19, TypeScript, and Vite
  • 🔌 Simple Integration - Single line of code to embed
  • 🛠️ Developer Experience - Hot reload, TypeScript, and modern tooling

How to install the widget in your web app

  1. Load the widget on your page:

    <script
      src="https://upvoted.io/widget/widget.js"
      defer
      data-client-key="YOUR_API_TOKEN"
      data-class-name="dark"
    ></script>
  2. Replace the data-client-key value with the API token generated from the Upvoted backoffice. Use a token with Read & Write permission; read-only tokens cannot create requests, comments, or upvotes.

  3. Set the data-class-name value with dark to load the widget in dark mode, otherwise leave it blank.

Embed the full board (new)

You can also embed the full board using the same script by switching the mode to board.

Minimal example (inserts after the script tag):

<script
  src="https://upvoted.io/widget/widget.js"
  defer
  data-mode="board"
  data-client-key="YOUR_API_TOKEN"
  data-class-name="dark"
></script>

Mount into a specific element:

<div id="upvoted-board-here"></div>
<script
  src="https://upvoted.io/widget/widget.js"
  defer
  data-mode="board"
  data-embed-target="#upvoted-board-here"
  data-height="700px"
  data-client-key="YOUR_API_TOKEN"
  data-class-name="dark"
  data-statuses="pending,in_progress,done"
></script>

Supported attributes in board mode:

  • data-mode: board to enable board embed. Default is widget.
  • data-embed-target (optional): CSS selector where the board should mount. If omitted, the board is appended to document.body after the script tag.
  • data-client-key: your API token (required), used as Authorization: Bearer <token>.
  • data-class-name (optional): wrapper class, e.g. dark.
  • data-height (optional): height to apply if the target has no explicit height. Defaults to 800px.
  • data-statuses (optional): comma-separated list of status names to include (lowercased). If empty or omitted, loads all statuses.
  • data-allow-feature-request (optional): when true, renders the standard request widget below the board while preserving the board layout. Requires an API token with Read & Write permission.
  • data-allow-feature-comment (optional): when true, shows a comment form inside the feature modal. Fields required: name, email, comment. Requires an API token with Read & Write permission.
  • data-allow-feature-upvote (optional): when true, shows an Upvote action in the feature modal. Upvoting requires a contributor object with both name and email. Requires an API token with Read & Write permission.

Example:

<div id="upvoted-board-here"></div>
<script
  src="https://upvoted.io/widget/widget.js"
  defer
  data-mode="board"
  data-embed-target="#upvoted-board-here"
  data-client-key="YOUR_API_TOKEN"
  data-allow-feature-request="true"
  data-allow-feature-comment="true"
  data-allow-feature-upvote="true"
></script>

Open the widget from your own button or link (new)

Use data-mode="custom" to suppress the default floating button and bind the modal to your own host-page element instead. Useful when you want the “Send feedback” trigger to look like part of your site (a styled button, a link in a menu, etc.).

<button id="open-feedback-btn" type="button">Send us feedback</button>

<script
  src="https://upvoted.io/widget/widget.js"
  defer
  data-mode="custom"
  data-trigger-id="open-feedback-btn"
  data-client-key="YOUR_API_TOKEN"
  data-class-name="dark"
></script>

Supported attributes in custom mode:

  • data-mode: custom to enable host-page trigger binding.
  • data-trigger-id (required): the id of the element on your page that opens the modal when clicked. Any element works: a button, a link or a plain div. It can be added to the page after the script has loaded (by your framework, for example); clicks on anything inside it count too. A div made focusable with tabindex="0" also opens the modal on Enter or Space. Without data-trigger-id the widget logs a warning and shows the default floating button.
  • data-client-key, data-class-name: same as widget mode.

Notes:

  • No floating button is rendered. The widget mounts a hidden host on document.body and only renders the modal when the trigger is clicked.
  • data-embed-target is ignored in custom mode (the modal is fixed-position and floats over the page).

Style your public board and widget with custom CSS (Max)

On the Max plan a board can carry its own stylesheet: open the board in Upvoted, then Settings → Custom CSS. It is served after ours on your public board, request pages, changelog and release pages, and the embeddable widget adds it inside its shadow root, after its own styles, in every mode: the floating button, custom, board and changelog. One stylesheet styles both and can override anything on them. It never applies inside Upvoted itself, and inside the widget it stays in the shadow root, so it never touches the rest of the page the widget sits on. Below Max the stylesheet is kept, but neither your pages nor the widget use it.

Write it against the names below. The rest of the markup is Tailwind utility classes that change as the product does; these names are kept stable on purpose, so a stylesheet built on them keeps working.

Variables

Set them on .upv-page, .upv-widget: .upv-page is the body of your public pages and .upv-widget the root of the widget, so a rule on both themes both, and a rule on one themes only that one. :root works on your pages but does not reach inside the widget. Anything you leave out keeps Upvoted’s look, in light and dark mode.

Dark mode, on your pages (the visitor’s choice, or the one your board’s theme sets) and in the widget (data-class-name="dark"), draws with --upv-primary-dark and --upv-primary-contrast-dark. Leave them out and it uses --upv-primary and --upv-primary-contrast, as in light mode. Corners and font are the same in both modes.

  • --upv-primary (default: Upvoted’s amber): Buttons, links, votes the visitor has cast, the current page in the menu, the selected changelog label and the column accents; in the widget, the submit button, focus rings and accents. Their lighter and darker shades follow it, in light mode and in dark mode unless –upv-primary-dark is set.
  • --upv-primary-contrast (default: white): Text on a button in –upv-primary.
  • --upv-primary-dark (default: --upv-primary): The same parts in dark mode, with their shades derived from it: set a lighter colour here if yours is too dark to read on a dark page.
  • --upv-primary-contrast-dark (default: --upv-primary-contrast): Text on a button in –upv-primary-dark, in dark mode.
  • --upv-radius (default: 0.5rem): Corners of cards and changelog entries. Buttons use three quarters of it, vote buttons half. In the widget it rounds the columns, the request pop-up and the changelog, and cards, buttons and fields take three quarters.
  • --upv-font (default: the system font): The font of the public pages and the widget.

Classes

Each says whether it is on your board pages, in the widget or on both. The widget’s dark mode (data-class-name="dark") puts .dark on an element inside .upv-widget.

  • .upv-page (board pages): The body of every public page: board, request, changelog and release.
  • .upv-widget (widget): The root of the widget, inside its shadow root. Its counterpart of upv-page.
  • .upv-launcher (widget): The widget’s floating button.
  • .upv-widget-panel (widget): The widget’s feedback form panel.
  • .upv-header (board pages): The top bar with your logo and the Board and Releases menu.
  • .upv-mobile-menu (board pages): The menu the top bar opens on a phone.
  • .upv-brand (board pages): Your logo or board name in the top bar.
  • .upv-nav-link (board pages): Every menu link in the top bar and the phone menu. The current one also has aria-current=”page”.
  • .upv-color-scheme-toggle (board pages): The light and dark mode button in the top bar.
  • .upv-user-menu (board pages): A signed-in visitor’s avatar menu in the top bar, and its section of the phone menu.
  • .upv-board (board pages): The board page behind the panel and columns. It carries the background set in the board settings.
  • .upv-board-header (board pages): The panel with the board’s name, description and buttons.
  • .upv-board-title (board pages): The board’s name in that panel.
  • .upv-board-toolbar (board pages): The row of buttons on the right of that panel: filter, layout and “Upvote a new feature”.
  • .upv-column (both): One status column.
  • .upv-column-header (both): The status name at the top of a column.
  • .upv-card (both): A request card, on the board, on a release’s page and on the widget’s board.
  • .upv-card-title (both): A request’s title on its card.
  • .upv-vote-button (both): The vote buttons on a card, and Upvote in the widget’s request pop-up. upv-voted is added once the visitor has voted.
  • .upv-status-badge (both): The status in a request’s detail.
  • .upv-tag (both): A label on a changelog entry.
  • .upv-request (both): The request pop-up, with its detail or its comments.
  • .upv-comment (both): One comment.
  • .upv-similar (board pages): The “Already asked for?” box offering similar requests while one is being filed.
  • .upv-changelog (both): The changelog page and a release’s page, and the widget’s changelog.
  • .upv-release (both): One changelog entry.
  • .upv-release-notes (both): The text of a changelog entry.
  • .upv-label-filter (both): The label chips above the changelog. The selected one has aria-current=”true” on the page and aria-pressed=”true” in the widget.
  • .upv-button-primary (both): The main buttons: “Upvote a new feature” and Save in the forms; in the widget, Submit Feature Request and Submit comment.
  • .upv-link (both): Text links in the forms, such as “or login”; in the widget, Open in Upvoted.

Example

/* Your colour in light and dark mode, corners and font, on your board pages
   and in the widget. */
.upv-page,
.upv-widget {
  --upv-primary: #2563eb;
  --upv-primary-contrast: #ffffff;
  --upv-primary-dark: #60a5fa;
  --upv-primary-contrast-dark: #0f172a;
  --upv-radius: 6px;
  --upv-font: "IBM Plex Sans", system-ui, sans-serif;
}

/* A heavier border on request cards. */
.upv-card {
  border-width: 2px;
}

/* Status names in capitals. */
.upv-column-header,
.upv-status-badge {
  text-transform: uppercase;
  letter-spacing: .04em;
}

A stylesheet can be up to 30000 characters. It may not contain < or @import; to use a web font, load it with @font-face instead. The board’s background colour or image from its settings is drawn on .upv-board, and a rule of yours there, such as .upv-board { background: #f8fafc; }, replaces it.

Changelog

2.4.0

  • Custom CSS (Max): --upv-primary-dark and --upv-primary-contrast-dark set the widget’s colour in dark mode, with the hover, accent and outlined-button shades derived from them. Without them dark mode keeps using --upv-primary and --upv-primary-contrast, as before. See Style your public board and widget with custom CSS.

2.3.0

  • Custom CSS (Max): the board’s custom CSS now applies inside the widget too, in every mode, after the widget’s own styles. The widget’s parts carry the same upv- classes as the board pages, plus .upv-widget, .upv-launcher and .upv-widget-panel, and the --upv- variables can be set on .upv-widget. See Style your public board and widget with custom CSS.

2.2.0

  • Custom mode: the data-trigger-id element can now be added to the page after the widget script loads, or re-rendered, and still opens the modal. A focusable div opens it on Enter or Space.

2.1.0

  • New mode: data-mode="custom" lets the host page open the feature-request modal from its own button, link, or any clickable element via data-trigger-id. The default FAB is suppressed so the trigger matches the host site’s styling.

2.0.1

  • Modal: fixed open in upvoted link
  • New attributes:
    • data-allow-feature-upvote: enable upvoting inside the modal. Requires contributor (name and email) to be provided.

2.0.0

  • Board embed UX: column headers styled to match Upvoted; primary color aligned (amber).
  • Cards: transparent thumbnails on white background; top-aligned crop; counters layout unified.
  • Modal:
    • Public/private vote split and total.
    • Image shows from top with white background.
    • Contributor name shown when available.
    • Improved styling (section borders, header accent).
    • Conditional public link (only when public_url exists).
  • New attributes:
    • data-allow-feature-request: render request widget below the board without breaking board height.
    • data-allow-feature-comment: show a required comment form (name, email, comment) inside the modal.
  • Docs: README and CONTRIBUTE updated; CDN version set to 2.0.0.

License

This project is licensed under the MIT License. See the LICENSE file for more information.

About Releasy CORP, the SaaS company

Releasy CORP, We Build SaaS and Productivity Tools!