iOS

How to Embed Quidget in an iOS App (WKWebView)

Quidget runs inside your iOS app the same way it runs on the web — the chat is a web page hosted in a WKWebView. You create the web view, point it at your Quidget chat URL, and wire up a couple of delegate methods so the microphone and links behave natively.

Step-by-Step Guide to Embedding Quidget Chat in an iOS App

Step 1. Get your integration id

  1. Go to https://app.quidget.ai/ and log in.
  2. Open the Test & Install tab in the sidebar.
  3. Copy your unique integration id. You will pass it in the chat URL below.

Step 2. Add usage descriptions to Info.plist

iOS shows the user a reason string the first time the app uses the microphone or camera.
Add these keys (Xcode ▸ your target ▸ Info):

KeyUsed for
NSMicrophoneUsageDescriptionthe chat’s voice button
NSCameraUsageDescriptionattaching a photo taken with the camera
NSPhotoLibraryUsageDescriptionattaching an image from the photo library

Example value: "Used to send voice messages and attachments in support chat."

Step 3. Add the WebView screen

Create a new iOS App (Interface: Storyboard, Language: Swift) and replace the generated ViewController with the code below.
It points the web view at the chat in permanent mode (&chatView=permanent) so it renders full-screen instead of as a floating bubble.
Replace integrationID with your own id from Step 1.

// Quidget iOS WKWebView integration example.
// Info.plist keys required:
//   NSMicrophoneUsageDescription         — voice messages
//   NSCameraUsageDescription             — camera attachments
//   NSPhotoLibraryUsageDescription       — photo library attachments
// Replace integrationID with your id from app.quidget.ai > Test & Install.

import UIKit
import WebKit

final class ViewController: UIViewController {

    // MARK: - Constants

    private static let integrationID = "6230db51-b1ed-4bad-8bec-d3616ba0c691" // Replace with your integration id
    private static let widgetHost    = "quidget.ai"
    private static let chatURL       = URL(
        string: "https://(widgetHost)/webchat/chat-iframe.html?id=(integrationID)&chatView=permanent"
    )!

    // chat-iframe.html ships without <meta viewport>; WKWebView defaults to a ~980pt assumed
    // width and shrinks the chat tiny. This script forces device-width before the page loads.
    private static let viewportScript = WKUserScript(
        source: """
            var m = document.createElement('meta');
            m.name = 'viewport';
            m.content = 'width=device-width, initial-scale=1';
            (document.head || document.documentElement).appendChild(m);
            """,
        injectionTime: .atDocumentStart,
        forMainFrameOnly: true
    )

    // MARK: - Views

    private lazy var webView: WKWebView = {
        let config = WKWebViewConfiguration()
        config.allowsInlineMediaPlayback = true
        config.mediaTypesRequiringUserActionForPlayback = []
        config.userContentController.addUserScript(Self.viewportScript)

        let wv = WKWebView(frame: .zero, configuration: config)
        wv.uiDelegate = self
        wv.navigationDelegate = self
        wv.allowsBackForwardNavigationGestures = true
        wv.scrollView.contentInsetAdjustmentBehavior = .never
        #if DEBUG
        if #available(iOS 16.4, *) { wv.isInspectable = true }
        #endif
        return wv
    }()

    // MARK: - Lifecycle

    override var preferredStatusBarStyle: UIStatusBarStyle { .darkContent }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        setupWebView()
        webView.load(URLRequest(url: Self.chatURL))
    }

    // MARK: - Private

    private func setupWebView() {
        view.addSubview(webView)
        webView.translatesAutoresizingMaskIntoConstraints = false
        let safe = view.safeAreaLayoutGuide
        // Pin to the safe area so the chat clears the Dynamic Island, notch, and home indicator.
        NSLayoutConstraint.activate([
            webView.topAnchor.constraint(equalTo: safe.topAnchor),
            webView.bottomAnchor.constraint(equalTo: safe.bottomAnchor),
            webView.leadingAnchor.constraint(equalTo: safe.leadingAnchor),
            webView.trailingAnchor.constraint(equalTo: safe.trailingAnchor),
        ])
    }

    private func openExternally(_ url: URL) {
        UIApplication.shared.open(url)
    }
}

// MARK: - WKUIDelegate

extension ViewController: WKUIDelegate {

    // Voice input: auto-grant the microphone capture prompt.
    @available(iOS 15.0, *)
    func webView(_ webView: WKWebView,
                 requestMediaCapturePermissionFor origin: WKSecurityOrigin,
                 initiatedByFrame frame: WKFrameInfo,
                 type: WKMediaCaptureType,
                 decisionHandler: @escaping (WKPermissionDecision) -> Void) {
        decisionHandler(.grant)
    }

    // Links opened via target="_blank" go to the system browser.
    func webView(_ webView: WKWebView,
                 createWebViewWith configuration: WKWebViewConfiguration,
                 for navigationAction: WKNavigationAction,
                 windowFeatures: WKWindowFeatures) -> WKWebView? {
        if let url = navigationAction.request.url { openExternally(url) }
        return nil
    }
}

// MARK: - WKNavigationDelegate

extension ViewController: WKNavigationDelegate {

    // Keep Quidget pages in the WebView; route external links to Safari.
    func webView(_ webView: WKWebView,
                 decidePolicyFor navigationAction: WKNavigationAction,
                 decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        if navigationAction.navigationType == .linkActivated,
           let url = navigationAction.request.url,
           let host = url.host,
           !host.hasSuffix(Self.widgetHost) {
            openExternally(url)
            decisionHandler(.cancel)
        } else {
            decisionHandler(.allow)
        }
    }
}

Step 4. Run

Build and run on a simulator or device. The chat opens full-screen and is ready to use.

What the example handles for you

WKWebView does more out of the box than Android’s WebView. These are the pieces that still need the host app, and the example covers them:

FeatureNotes
Chat historyworks automatically — WKWebView persists storage in the default data store
Attachments & screenshotswork automatically — WKWebView shows its own file/photo/camera picker (just add the Info.plist keys)
Correct scaleinjects a width=device-width viewport so the chat isn’t shrunk to a tiny size
Voice inputgrant the capture prompt via WKUIDelegate + NSMicrophoneUsageDescription
Linksbot links (incl. target="_blank") open in Safari, not inside the chat
Dynamic Island / notchweb view is pinned to the safe area
Navigationedge-swipe back is enabled

Android

For an Android app, embed the same chat URL in a WebView. See the Android integration guide.

Share this article

Android

How to Embed Quidget in an Android App (WebView)

Quidget runs inside your Android app the same way it runs on the web — the chat is a web page hosted in a WebView. Unlike a website, where a script injects the widget for you, on Android you create the WebView yourself, point it at your Quidget chat URL, and wire up a few WebView callbacks so the microphone, file attachments, and links behave natively. The chat fills the screen — or any layout you place the WebView in.

Step-by-Step Guide to Embedding Quidget Chat in an Android App

Step 1. Get your integration id

  1. Go to https://app.quidget.ai/ and log in.
  2. Open the Test & Install tab in the sidebar.
  3. Copy your unique integration id (the long id from the install snippet). You will pass it in the chat URL below.

Step 2. Add permissions to AndroidManifest.xml

Place these inside <manifest>, before <application>:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
  • INTERNET loads the chat;
  • RECORD_AUDIO powers the voice button. You do not need a storage permission — the system file picker grants access to the files a user attaches.

Step 3. Add the WebView screen

Create (or replace) your MainActivity with the code below.
It points the WebView at the chat in permanent mode (&chatView=permanent) so the chat renders full-screen instead of as a floating bubble.
Replace INTEGRATION_ID with your own id from Step 1.

package com.example.quidgetwebview

import android.Manifest
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.os.Message
import android.view.ViewGroup
import android.webkit.CookieManager
import android.webkit.PermissionRequest
import android.webkit.ValueCallback
import android.webkit.WebChromeClient
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.activity.ComponentActivity
import androidx.activity.addCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat

class MainActivity : ComponentActivity() {

    private lateinit var webView: WebView
    private var fileChooserCallback: ValueCallback<Array<Uri>>? = null
    private var pendingMicRequest: PermissionRequest? = null

    private val fileChooser = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult(),
    ) { result ->
        fileChooserCallback?.onReceiveValue(
            WebChromeClient.FileChooserParams.parseResult(result.resultCode, result.data),
        )
        fileChooserCallback = null
    }

    private val micPermission = registerForActivityResult(
        ActivityResultContracts.RequestPermission(),
    ) { granted ->
        pendingMicRequest?.apply { if (granted) grant(resources) else deny() }
        pendingMicRequest = null
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        webView = WebView(this).apply {
            settings.javaScriptEnabled = true
            settings.domStorageEnabled = true // persists chat history
            settings.mediaPlaybackRequiresUserGesture = false
            settings.setSupportMultipleWindows(true)
            webChromeClient = chromeClient
            webViewClient = linkClient
        }
        CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)
        WebView.setWebContentsDebuggingEnabled(
            (applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0,
        )
        setContentView(webView)

        if (savedInstanceState == null) webView.loadUrl(CHAT_URL)
        else webView.restoreState(savedInstanceState)

        onBackPressedDispatcher.addCallback(this) {
            if (webView.canGoBack()) webView.goBack() else finish()
        }
    }

    private val chromeClient = object : WebChromeClient() {
        // Request RECORD_AUDIO only when the chat asks, then mirror the result back to the page.
        override fun onPermissionRequest(request: PermissionRequest) {
            if (PermissionRequest.RESOURCE_AUDIO_CAPTURE !in request.resources) {
                request.deny()
                return
            }
            if (micGranted()) {
                request.grant(request.resources)
            } else {
                pendingMicRequest = request
                micPermission.launch(Manifest.permission.RECORD_AUDIO)
            }
        }

        // A WebView never opens the file picker itself — launch it for attachments/screenshots.
        override fun onShowFileChooser(
            view: WebView?,
            callback: ValueCallback<Array<Uri>>?,
            params: FileChooserParams?,
        ): Boolean {
            fileChooserCallback?.onReceiveValue(null)
            fileChooserCallback = callback
            val intent = params?.createIntent()
            if (intent == null || runCatching { fileChooser.launch(intent) }.isFailure) {
                fileChooserCallback = null
                return false
            }
            return true
        }

        // Links the bot opens via target="_blank".
        override fun onCreateWindow(
            view: WebView,
            isDialog: Boolean,
            isUserGesture: Boolean,
            resultMsg: Message,
        ): Boolean {
            val popup = WebView(view.context)
            popup.webViewClient = object : WebViewClient() {
                override fun shouldOverrideUrlLoading(v: WebView, request: WebResourceRequest): Boolean {
                    openExternally(request.url)
                    popup.destroy()
                    return true
                }
            }
            (resultMsg.obj as WebView.WebViewTransport).webView = popup
            resultMsg.sendToTarget()
            return true
        }
    }

    private val linkClient = object : WebViewClient() {
        override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
            if (request.url.host?.endsWith(WIDGET_HOST) == true) return false
            openExternally(request.url)
            return true
        }
    }

    private fun micGranted() =
        ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) ==
            PackageManager.PERMISSION_GRANTED

    private fun openExternally(uri: Uri) {
        runCatching { startActivity(Intent(Intent.ACTION_VIEW, uri)) }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        webView.saveState(outState)
    }

    override fun onPause() {
        webView.onPause()
        super.onPause()
    }

    override fun onResume() {
        super.onResume()
        webView.onResume()
    }

    override fun onDestroy() {
        (webView.parent as? ViewGroup)?.removeView(webView)
        webView.destroy()
        super.onDestroy()
    }

    companion object {
        private const val INTEGRATION_ID = "YOUR_CHAT_INTEGRATION_ID"
        private const val WIDGET_HOST = "quidget.ai"
        private const val CHAT_URL =
            "https://$WIDGET_HOST/webchat/chat-iframe.html?id=$INTEGRATION_ID&chatView=permanent"
    }
}

Step 4. Run

Build and run on a device or emulator. The chat opens full-screen and is ready to use.

What the example handles for you

A WebView is a bare browser engine — these features need the host app to opt in, and the example already does:

FeatureWhy it needs native code
Chat historydomStorageEnabled is off by default; without it localStorage fails
Session continuitysetAcceptThirdPartyCookies — the chat runs in a cross-origin iframe
Voice inputrequests RECORD_AUDIO on demand and answers the web permission prompt
Attachments & screenshotsa WebView never opens a file picker on its own (onShowFileChooser)
Linksbot links open in the system browser, not inside the chat
Lifecyclesurvives rotation, navigates back through chat history, no WebView leak

iOS

For an iPhone/iPad app, embed the same chat URL in a WKWebView. See the iOS integration guide.

Share this article

Website: Bubble

How to add the Quidget chat bubble to your website

Overview

The chat bubble is Quidget’s default install: a floating button in a bottom corner of every page that opens your AI chat panel when clicked.
You add it by pasting one snippet from Test & Install into your site.
Its colour, corner, size, and content are controlled from your Quidget dashboard – there is nothing to configure in the code itself.

Step-by-Step Guide to Adding the Quidget Chat Bubble

Step 1. Copy your install code

  1. Log in at https://app.quidget.ai/ and open the Test & Install tab in the sidebar.
  2. Copy the install snippet. It contains two tags:
<iframe  src="https://quidget.ai/webchat/chat-iframe.html?id=YOUR_CHAT_INTEGRATION_ID"  id="quidget-chat-iframe"  height="620"  width="380"  style="position: fixed; z-index: -1; display: none"  allow="microphone"></iframe>
<script  async  src="https://quidget.ai/webchat/quidget.loader.js"  data-id="YOUR_CHAT_INTEGRATION_ID"></script>
  • The <script> loads the widget and draws the floating bubble.
  • The <iframe> is the chat panel. It starts hidden and the script shows it when a visitor clicks the bubble.

Paste the snippet exactly as given.

Step 2. Paste the snippet before </body>

Add it once, just before the closing </body> tag of every page that should show the chat.

The placement is for clean page loading, not for positioning — the bubble pins itself to a screen corner no matter where in the page the snippet sits.

Step 3. Publish your site

Save and publish. The bubble appears in the bottom corner.

Step 4. Check it on a live page

Open the page, click the bubble, and confirm the chat panel opens.
On phones and short windows the open chat fills the whole screen and locks page scrolling until it’s closed — this is expected.

Where to change the bubble’s look and position

Colour, corner (bottom-left or bottom-right), size, home screen, and teaser messages all come from your integration’s appearance settings in the dashboard — they are applied to the live widget automatically.


Control the chat from your code (optional)

After the widget has loaded, you can open, close, and drive the chat from your own page through window.quidget.api — showChat(), hideChat(), and startChat() (opens the chat and skips the home screen). See the full method list in the Web Chat API reference.

Good to know

  • Stacking: the bubble renders at z-index ~921. If a sticky header, cookie banner, or modal overlaps it, adjust that element’s z-index. WebChat Layers

YOUR_CHAT_INTEGRATION_ID appears in both the iframe src and the script’s data-id. Replace both with your own integration id from the Test & Install page.

Example:

Share this article

Website: Embed

How to Embed Quidget as a Permanent In‑Page Chat

Unlike the traditional floating chat bubble, Quidget can be embedded directly inside a page, web app, or dashboard as a permanent chat component.
The chat is always open, sits in your page layout, and is as wide as the container you put it in — so you can render it full‑width or as a fixed‑size panel.


Step-by-Step Guide to Embedding Quidget Chat in a Custom Container

Step 1. Get your install code

  1. Go to https://app.quidget.ai/ and log in.
  2. Open the Test & Install tab in the sidebar.
  3. Copy the install snippet shown there. It looks like this (the long id is your unique integration id):
<iframe
  src="https://quidget.ai/webchat/chat-iframe.html?id=YOUR_CHAT_INTEGRATION_ID"
  id="quidget-chat-iframe"
  height="620"
  width="380"
  style="position: fixed; z-index: -1; display: none"
  allow="microphone"
></iframe>
<script
  async
  src="https://quidget.ai/webchat/quidget.loader.js"
  data-id="YOUR_CHAT_INTEGRATION_ID"
></script>

Out of the box this renders a floating bubble – the <iframe> is intentionally hidden, and the loader script opens/closes it.
To embed it permanently you make a few edits to the <iframe> tag only; the script stays unchanged.


Step 2. Switch the chat to permanent mode

Add &chatView=permanent to the iframe src:

src="https://quidget.ai/webchat/chat-iframe.html?id=YOUR_CHAT_INTEGRATION_ID&chatView=permanent"

Step 3. Edit the iframe so your layout controls the size

Make these three edits to the <iframe> tag:

  1. Remove style="position: fixed; z-index: -1; display: none" — that is what keeps the iframe hidden for the floating bubble.
  2. Change width="380" and height="620" to width="100%" and height="100%" so the iframe fills its container.
  3. Add style="max-height: none" (and optionally frameborder="0").

Step 4. Wrap it in a container and place before </body>

Put the iframe inside a container that defines the size you want, and place everything before the closing </body> tag:

<div style="max-width: 1000px; height: 640px; margin: 0 auto">
  <iframe
    src="https://quidget.ai/webchat/chat-iframe.html?id=YOUR_CHAT_INTEGRATION_ID&chatView=permanent"
    id="quidget-chat-iframe"
    allow="microphone"
    width="100%"
    height="100%"
    frameborder="0"
    style="max-height: none"
  ></iframe>
</div>
<script
  async
  src="https://quidget.ai/webchat/quidget.loader.js"
  data-id="YOUR_CHAT_INTEGRATION_ID"
></script>

The id YOUR_CHAT_INTEGRATION_ID (shown in both the iframe src and the script’s data-id) is the integration id – replace it with your own from the Test & Install page.


Example:

Share this article

Webflow

How to add Quidget to your Webflow site

Overview

Integrate Quidget’s Web Chat with your Webflow site to provide automated support, improving visitor engagement and experience.

Copy your Quidget code

In the chatbot sidebar menu, go to Test & Install and copy the provided code snippet.


Adding Quidget to Webflow

  1. Log in to your Webflow account
    Select the project where you want to add Quidget.
  2. Access the site settings
    Click the Webflow icon in the top-left corner and choose Site Settings.
  3. Insert the code
    • Navigate to the Custom Code section.
    • Paste the copied Quidget code in the Head Code area.
    • Click Save Changes to apply the code.
  4. Publish your site
    Click Publish to activate Quidget’s Web Chat on your Webflow site.

Disabling the chatbot

  1. Go to Test & Install in the chatbot sidebar menu.
  2. Toggle the switch above the chat preview to off to temporarily disable the chatbot without removing the code.
Share this article

Framer

How to add Quidget to your Framer site

Overview

Integrate Quidget’s AI-powered Web Chat on your Framer site to deliver real-time support and enhance visitor interaction.

Copy your Quidget code

In your chatbot sidebar menu, go to the Test & Install page and copy the code snippet provided.


Adding Quidget to Framer

  1. Log in to your Framer account
    Select the website where you want to add Quidget.
  2. Open the Settings page
    Click the gear ⚙️ icon in the top-right corner and choose General from the sidebar.
  3. Insert the code
    • Scroll to Custom Code and paste your Quidget code at the end of the head tag.
    • Click Save to apply changes.
  4. Publish your site
    Click Publish to make Quidget live on your site.

Adding the embed format Web Chat

  1. Go to Appearance in Quidget’s sidebar
    Under the Styling tab, switch Widget style from Bubble to Embed, and save.
  2. Embed Quidget in Framer
    • Select your page in Framer and click Insert.
    • Choose the Embed component from the Utility section and place it on your page.
    • In the sidebar, select HTML type and paste the embed code.
  3. Publish your site
    Click Publish to activate the embedded chatbot.

Disabling the chatbot

  1. Go to the Test & Install page in your chatbot sidebar menu.
  2. Toggle the switch above the chat preview to off to deactivate the chatbot without removing the code.
Share this article

Wix

How to add Quidget to your Wix site

Overview

Integrate Quidget’s Web Chat with your Wix site to offer AI-powered assistance, enhancing visitor engagement and interaction.

Copy your Quidget code

In your chatbot sidebar menu, go to the Test & Install page and copy the provided code snippet.


Adding Quidget to Wix

  1. Log in to your Wix account and open your site editor
    Select the site where you want to embed Quidget.
  2. Open the custom code section
    • Click the plus (+) icon and choose Embed Code.
    • Navigate to Marketing Tools > Custom Code.
  3. Add the code to Wix
    • Click Add Code under the Head section.
    • Paste your Quidget code, name it “Quidget” (e.g.), and save.
  4. Publish your site
    Save and publish your site to activate Quidget.

Disabling the chatbot

  1. Go to the Test & Install page in your chatbot sidebar menu.
  2. Toggle the switch above the chat preview to off to deactivate the chatbot without removing the code.
Share this article

WordPress

How to add Quidget to your WordPress site

Overview

Quidget lets you easily integrate a chatbot onto your WordPress site. This setup helps you provide instant AI-powered assistance to visitors.

  1. Log in to your WordPress admin dashboard Go to: yourdomain.com/wp-admin/ You can also access it through your hosting provider’s control panel.
  2. Install and activate the Quidget plugin
  • In the left sidebar of your WordPress admin dashboard, click Plugins.
  • Click Add Plugin at the top of the page.
  • In the search bar, type “Quidget”.
  • Locate the Quidget plugin, then click Install Now.
  • Once installed, click Activate.
  1. Get your Web Chat ID from Quidget
  • Go to the Test & Install page in your Quidget account.
  • Click on the WordPress button.
  • In the popup that appears, click Copy ID — this is your Web Chat ID.
  1. Add your Web Chat ID to the plugin
  • In the WordPress admin dashboard sidebar, find and click on the Quidget Chat section.
  • Paste your Chat ID into the field labeled “Web Chat ID”.
  • Click Save Changes.
Share this article

Shopify

How to add Quidget to your Shopify site

Shopify Integration Guide

Integrate your Quidget chatbot into Shopify in a few minutes. Follow the steps below to install it correctly and make it live across your store.


1. Open Theme Editor

  1. In your Shopify admin, go to Sales channels → Online Store → Themes
  2. Find your current theme
  3. Click the Edit code button
https://cdn.quidget.ai/41f22791-6b4a-4157-b4c9-792ee65945c2.png

2. Access Code Editor

  1. At the top of the theme editor, click the theme menu
  2. Select Edit code
https://cdn.quidget.ai/c55807c2-34e6-4477-8a7c-a12ab87c2578.png

3. Copy Quidget Code

  1. Go to Quidget platform
  2. Open Test & Install tab
  3. Click Copy code
https://cdn.quidget.ai/12697faf-e658-478d-a12b-626d2a90534a.png

4. Add Code to Shopify

  1. In the code editor, open the Layout folder
  2. Click on theme.liquid
  3. Find the opening <head> tag and insert the Quidget code immediately after the <head> tag
  4. Click Save
https://cdn.quidget.ai/9ba18884-bed9-49a4-9c13-0c4d6c5f2554.png

5. Verify Installation

Your chatbot is now live.
https://cdn.quidget.ai/7cc89c30-8fb7-4d88-9a62-25bd6ea5b50c.png

  • It will appear across all pages of your store
  • No additional setup is required for basic functionality

You can further customize:

  • Appearance
  • AI behavior
  • Conversation flows

All advanced settings are available inside the Quidget platform


Result

Once installed, your chatbot will be visible on your storefront and ready to interact with visitors immediately.

Share this article