> ## Documentation Index
> Fetch the complete documentation index at: https://docs.feedal.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Embed on a website

> Add your form to any website using the embed script — choose a display mode, trigger, and animation.

The Feedal embed script lets you add a form to any website with a single `<script>` tag. Choose from ten display modes — floating button, popup, drawer, fullscreen, and more — and configure how and when it appears.

Go to **Share → Embed** to configure and copy the code. Embedding requires the **Pro plan**.

***

## Quick start

Paste this into your HTML `<head>` or before `</body>`, replacing the config values with your own:

```html theme={null}
<script>
  (function() {
    const config = {
      "formId": "your-form-slug",
      "mode": "button",
      "trigger": "auto"
    };
    const script = document.createElement('script');
    script.async = true;
    script.src = 'https://feedal.com/embed/v1/feedal.js';
    script.onload = function() { window.Feedal.init(config); };
    document.head.appendChild(script);
  })();
</script>
```

The embed configurator in the dashboard generates this snippet for you — just copy and paste.

***

## Display modes

| Mode         | Description                                | Best for                   |
| ------------ | ------------------------------------------ | -------------------------- |
| `button`     | Floating action button that opens the form | Always-available feedback  |
| `popup`      | Modal dialog with overlay                  | Quick feedback prompts     |
| `modal`      | Enhanced dialog with more layout options   | Important surveys          |
| `embedded`   | Seamlessly placed inside a page section    | Content feedback           |
| `inline`     | Direct embedding within article content    | Article feedback           |
| `fullscreen` | Full viewport takeover                     | Onboarding, deep surveys   |
| `drawer`     | Slides in from a screen edge               | Mobile-friendly panels     |
| `sidebar`    | Full-height side panel                     | Navigation, detailed forms |
| `slide-over` | Professional side panel overlay            | Enterprise applications    |
| `toast`      | Lightweight notification-style widget      | Quick ratings, alerts      |

***

## Trigger types

The trigger controls **when** the widget appears.

| Trigger            | Opens when                                                |
| ------------------ | --------------------------------------------------------- |
| `manual`           | You call `window.Feedal.open()` from your own code        |
| `auto`             | Page loads (with optional delay in seconds)               |
| `time`             | Visitor has been on the page for N seconds                |
| `scroll`           | Visitor has scrolled N% down the page                     |
| `exit-intent`      | Visitor moves cursor toward the browser chrome (desktop)  |
| `element-visible`  | A specific CSS selector comes into the viewport           |
| `session-duration` | Visitor has been on the site (across pages) for N seconds |
| `idle`             | Visitor has been inactive for N seconds                   |

***

## Configuration reference

All configuration keys that differ from their defaults are included in the generated snippet. The full list:

| Key                   | Default         | Description                                                                                           |
| --------------------- | --------------- | ----------------------------------------------------------------------------------------------------- |
| `formId`              | —               | **Required.** Your form's slug (e.g. `my-feedback-form`) — found in **Share → Embed** or the form URL |
| `mode`                | —               | **Required.** Display mode (see above)                                                                |
| `trigger`             | —               | **Required.** Trigger type (see above)                                                                |
| `theme`               | `light`         | `light` or `dark`                                                                                     |
| `animation`           | `fade`          | `fade` · `slide` · `scale` · `bounce` · `flip` · `elastic` · `none`                                   |
| `position`            | `center`        | Widget position on screen (e.g. `bottom-right`, `center`)                                             |
| `width`               | `400px`         | Widget width                                                                                          |
| `height`              | `600px`         | Widget height                                                                                         |
| `overlay`             | `false`         | Show a dimmed overlay behind the widget                                                               |
| `showCloseButton`     | `true`          | Show an × close button                                                                                |
| `closeOnOverlayClick` | `false`         | Close when clicking outside the widget                                                                |
| `responsive`          | `true`          | Adapt layout for small screens                                                                        |
| `triggerDelay`        | `1`             | Seconds to wait before auto/time trigger fires                                                        |
| `triggerThreshold`    | `50`            | Scroll percentage or visibility percentage for scroll/element-visible triggers                        |
| `triggerElement`      | —               | CSS selector for `element-visible` trigger                                                            |
| `triggerCooldown`     | `5`             | Minutes before the widget can appear again (after close)                                              |
| `draggable`           | `false`         | Allow respondents to drag the widget                                                                  |
| `blurBackground`      | `false`         | Blur the page behind the overlay                                                                      |
| `autoClose`           | `true`          | Auto-close the widget 3 seconds after submission                                                      |
| `ariaLabel`           | `Feedback Form` | ARIA label for accessibility                                                                          |
| `focusTrap`           | `true`          | Trap keyboard focus inside the widget when open                                                       |
| `rememberSubmission`  | `true`          | Hide the widget for users who have already submitted (see below)                                      |
| `submissionExpiry`    | `30`            | Days before a remembered submission expires and the form shows again                                  |
| `storageType`         | `local`         | Where to store the submission record — `local`, `session`, or `cookie`                                |

***

## Submission persistence

By default the widget remembers users who have already submitted your form and won't show it to them again for 30 days. This works entirely client-side — no extra network requests.

### How it works

1. When the form is submitted, the widget writes a record to `localStorage` (or `sessionStorage` / a cookie, depending on `storageType`). The record contains the form's **session key** and two timestamps:
   * `cached_until` — 24 hours from submission. Within this window the widget skips opening immediately, with zero network cost.
   * `expires_at` — `submissionExpiry` days from submission. After this the form shows again as if it were a fresh visit.

2. After the 24-hour cache window expires (but before `submissionExpiry`), the widget silently creates a hidden iframe to re-check the session key. If the key still matches, the 24-hour window is extended. If the key has changed (because a form manager reset it), the cache is cleared and the form shows again.

### Forcing all users to see the form again

If you've changed your form content and want all prior visitors to see it again regardless of their remembered submission, go to **Settings → Submission Persistence** and click **Reset key**. All embed widgets will detect the new key on their next visit and show the form.

<Info>
  Session key reset requires the **Pro plan**.
</Info>

### Disabling persistence

Pass `rememberSubmission: false` to always show the form, regardless of prior submissions:

```js theme={null}
window.Feedal.init({
  formId: "your-form-slug",
  mode: "button",
  trigger: "auto",
  rememberSubmission: false,
});
```

***

## Callbacks

React to widget events from your own JavaScript:

```js theme={null}
window.Feedal.init({
  formId: "your-form-slug",
  mode: "popup",
  trigger: "scroll",
  onLoad:    () => console.log("Form ready"),
  onOpen:    () => console.log("Widget opened"),
  onClose:   () => console.log("Widget closed"),
  onSubmit:  (data) => console.log("Submitted", data),
  onSkipped: () => console.log("Skipped — user already submitted"),
  onError:   (err) => console.error("Error", err),
});
```

| Callback    | Fires when                                                                                 |
| ----------- | ------------------------------------------------------------------------------------------ |
| `onLoad`    | The form has loaded and is ready to display                                                |
| `onOpen`    | The widget becomes visible                                                                 |
| `onClose`   | The widget is closed                                                                       |
| `onSubmit`  | The respondent submits the form                                                            |
| `onSkipped` | The widget was suppressed because this user already submitted (see Submission persistence) |
| `onError`   | An error occurs during loading                                                             |

***

## React / Next.js

Use the embed script inside a `useEffect` in a client component:

```tsx theme={null}
"use client";
import { useEffect } from "react";

export default function FeedbackWidget() {
  useEffect(() => {
    const config = {
      formId: "your-form-slug",
      mode: "button",
      trigger: "auto",
    };
    const script = document.createElement("script");
    script.src = "https://feedal.com/embed/v1/feedal.js";
    script.async = true;
    script.onload = () => window.Feedal.init(config);
    document.head.appendChild(script);

    return () => {
      if (window.Feedal?.destroy) window.Feedal.destroy();
    };
  }, []);

  return null;
}
```

***

## Programmatic control

Once the script has loaded you can control the widget from your own JavaScript:

```js theme={null}
window.Feedal.open();    // Open the widget
window.Feedal.close();   // Close the widget
window.Feedal.destroy(); // Remove the widget from the page
```

***

## Next steps

<CardGroup cols={2}>
  <Card title="Email embed" icon="envelope" href="/sharing/email-embed">
    Get a ready-to-use email template for your campaigns.
  </Card>

  <Card title="Public link" icon="link" href="/sharing/public-link">
    Share the direct URL instead of an embed.
  </Card>
</CardGroup>
