embed waiver widget for your site

Usage no npm install needed!

<script type="module">
  import waiverforeverWaiverWidget from 'https://cdn.skypack.dev/@waiverforever/waiver-widget';


WaiverForever Waiver Widget

Adding a waiver widget to your website to allow participants to sign without leaving the page.



  1. Choose the preferred waiver templates from your templates.
  2. Go to Template Settings page -> Waiver Widget tab.
  3. Enable the widgets and configure them.
  4. Keep the templateIds for the next steps.
  5. There are multiple ways of embedding our waiver widget into your website:

Want to add it to your WordPress website? Click here to see our tutorial.

Easy route: reference the widget script from CDN:

We can load the widget automatically or manually.

For automatically loading, append the following HTML snippet into the body tag of your website:

<script src="https://cdn.waiverforever.com/qs3/ew.js?templateId=${templateId1,templateId2}"></script>
  1. Widget with its trigger button will be mounted as soon as the script is loaded. It exposes the widget instance as a global JavaScript variable named waiverWidget (note: its initial letter is lowercase) and WF_EMBED_WAIVER (for backward compatibility).
  2. If you need multiple waivers be signed one by one, you can set multiple template IDs by join them with comma.
  3. The widget instance has a hook method .onReady, which you can use to perform extra operations if needed.
  4. If you don't like the default triggering button and want to create your own way to trigger the waiver modal, simply adding a query sdkMode=yes to disable it:
    <script src="https://cdn.waiverforever.com/qs3/ew.js?templateId=${templateId1,templateId2}&sdkMode=yes"></script>

For manually loading, append the following HTML snippet into the body tag of your website:

<script src="https://cdn.waiverforever.com/qs3/ew.js"></script>
  1. If the script's src doesn't contain a specified templateId, the script exposes the widget constructor as a global JavaScript variable named WaiverWidget (note: its initial letter is uppercase).
  2. You could manually initialize the widget instance on demand. Please make sure performing any extra operations after the .onReady hook is triggered.

You could also load the widget script dynamically, e.g. The AMD way:

require(['https://cdn.waiverforever.com/qs3/ew.js'], function (WaiverWidget) {
        const widget = new WaiverWidget({
            templateId: `${templateId}`, // You can also pass multiple template ids as an array, they will be displayed one after signing one.
            sdkMode: true

            myCustomButton.addEventListener('click', function () {

Importing the widget into the module system

Install the widget by npm or yarn:

# npm
npm i --save @waiverforever/waiver-widget
# yarn
yarn add @waiverforever/waiver-widget
// app.js
const WaiverWidget = require('@waiverforever/waiver-widget')

const widget = new WaiverWidget()
  1. TypeScript is fully supported.
  2. Definitions bring TS/JS users the better developing experience.
  3. Could be used in ES modules or CommonJS standards systems.
  4. Could be dynamically loaded by third-party bundler's code splitting feature.



  • widget constructor: In manually loading or dynamically loading scenarios, the script exposes a constructor named WaiverWidget as a global variable. In a module system, we could require or import it directly.
  • widget instance: Instance of widget constructor. We could register events to its hooks and manipulate the instance through its methods.
  • SDK mode: Widget without the default triggering button, you need to programmatically trigger the waiver modal.
  • window mode: When enabled, instead of showing waiver modal, a new waiver page will be opened. If you passed multiple waiver template ID the window mode will be turn off.


WaiverWidget constructor

interface WidgetOption {
    templateId: string | string[];  // required, template id, or pass multiple ids
    sdkMode?: boolean;  // by default is false
    windowMode?: boolean; // by default it depends on the useragent (true for mobile devices, false for desktops)
    stylesheetURL?: string; // specify the stylesheet URL of the embedded waiver

type WaiverWidgetConstructor = new (option?: WidgetOption): IWidget;


const widget = new WaiverWidget({
    templateId: `${templateId}`,

WaiverWidget instance

interface IWidget {
    closeEmbedWaiverModal: () => void;  // Legacy API, for backward compatibility
    show: () => void; // Show widget modal and hide trigger button(in non-sdk mode)
    close: () => void; // Opposite operation of show()
    toggle: () => void; // Toggle between show and close methods
    destroy: () => void; // Destroy the current widget instance. After ensuring previous widget instance is destroyed, we can re-initialize another widget instance.
    onShowed: (callback: () => void) => void; // Event registry for modal showed hook
    onReady: (callback: (widget: IWidget) => void) => void; // Event registry for widget instance ready hook, an interactive widget instance will be passed to the registered hook
    onLoad: (callback: (signedCount: number) => void) => void; // Event registry for waiver page loading in the SDK mode
    onSigned: (callback: (signedCount: number) => void) => void; // Event registry for waiver signing success hook
    onClosed: (callback: (signedCount: number) => void) => void; // Event registry for modal close hook, the registered callback will receives a boolean type argument presents whether the user signed or not
    onDestroyed: (callback: () => void) => void; // Event registry for widget destroyed hook


If you get bored of using the callback style .onReady hook and others, and the async/await syntax is available in your runtime, here are some tips for you.

All of the widget APIs are thenable/awaitable! It means you can use the widget instance, instance methods and event hooks in a synchronous-like way.

Thenable/awaitable instance

Awaiting the response of the widget instance, we get a ready state (fully-interactive) instance.

// note: use in async function, or some `top-level await` syntax implemented environment
async function appDidMount () {
    const widget = await new WaiverWidget({
        templateId: `${templateId}`, // You can also pass multiple template ids  as an array, they will be displayed one after signing one.

    widget.show() // widget is in ready state

Thenable/awaitable instance methods

Awaiting a response of a widget method, we receive the callback payload of the method's corresponding event hook.

async function appShowWaiverInstruction () {
    // `.onShowed` event has no return value
    await widget.show()
    sendLog('waiver showed')
async function appCloseWaiverInstruction () {
    // We get the signed count, which is the `.onClosed` event's callback payload
    const signedCount = await widget.close()
    sendLog('waiver closed, with signed count: ' + signedCount)

Thenable/awaitable event hooks

A registered event handler will be triggered whenever the event occurs. An awaited event hook will only respond to the event payload for once.

async function appDidMount () {
    // ... initialize widget instance ...
    widget.onSigned(async function () {
        // signedCount presents the number you signed successfully
        const signedCount = await widget.onClosed
        // Log will be sent under the condition that waiver is signed and then the modal is closed
        sendLog('waiver closed, with signed count: ' + signedCount)

Actually, awaiting a response of a event hook is rarely used.