Vue logo

Vue Integration

Embed the Maison concierge chat widget in a Vue 3 application. Both the Composition API and Options API are supported — choose the pattern that matches your codebase. Vue official site ↗

Prerequisites

  • Vue 3 (Composition API or Options API)
  • Your Client UUID — find it in the Business Console under Settings
  • A client-side rendering context (see SSR note below)

Steps

  1. Create MaisonChat.vue using the Composition API

    Create src/components/MaisonChat.vue. The script is injected in onMounted and cleaned up in onUnmounted.

    vue
    <!-- MaisonChat.vue — Composition API (script setup) -->
    <template>
      <!-- This component renders nothing; it only manages the script lifecycle. -->
    </template>
    
    <script setup lang="ts">
    import { onMounted, onUnmounted, watch } from 'vue';
    
    const props = defineProps<{
      clientId: string;
    }>();
    
    let scriptEl: HTMLScriptElement | null = null;
    
    function injectScript(clientId: string): void {
      scriptEl = document.createElement('script');
      scriptEl.src = 'https://agent.maison-labs.com/agent-inject.bundle.js';
      scriptEl.onload = () => {
        const sessionId = new URLSearchParams(window.location.search).get('mcbsid');
        const mcbs = (window as any).initMcbs({
          clientId,
          sessionId,
          // locale: 'en-US',                     // Override browser locale
          // iconPosition: { top: 0, bottom: 20, left: 0, right: 20 },  // Chat icon position (px)
          // onSessionUpdate: (id) => console.log('Session:', id),
          // onLocaleUpdate: (locale) => console.log('Locale:', locale),
        });
        mcbs.showIcon();
      };
      document.head.appendChild(scriptEl);
    }
    
    function removeScript(): void {
      if(scriptEl) {
        document.head.removeChild(scriptEl);
        scriptEl = null;
      }
    }
    
    onMounted(() => injectScript(props.clientId));
    onUnmounted(() => removeScript());
    
    watch(
      () => props.clientId,
      (newId) => {
        removeScript();
        injectScript(newId);
      },
    );
    </script>
  2. Alternatively — Options API pattern

    If your project uses the Options API, use the mounted and unmounted lifecycle hooks instead.

    vue
    <!-- MaisonChat.vue — Options API -->
    <template>
      <!-- This component renders nothing; it only manages the script lifecycle. -->
    </template>
    
    <script lang="ts">
    import { defineComponent } from 'vue';
    
    export default defineComponent({
      name: 'MaisonChat',
      props: {
        clientId: {
          type: String,
          required: true,
        },
      },
      data() {
        return {
          scriptEl: null as HTMLScriptElement | null,
        };
      },
      mounted() {
        this.injectScript(this.clientId);
      },
      unmounted() {
        this.removeScript();
      },
      watch: {
        clientId(newId: string) {
          this.removeScript();
          this.injectScript(newId);
        },
      },
      methods: {
        injectScript(clientId: string): void {
          const script = document.createElement('script');
          script.src = 'https://agent.maison-labs.com/agent-inject.bundle.js';
          script.onload = () => {
            const sessionId = new URLSearchParams(window.location.search).get('mcbsid');
            const mcbs = (window as any).initMcbs({
              clientId,
              sessionId,
              // locale: 'en-US',                     // Override browser locale
              // iconPosition: { top: 0, bottom: 20, left: 0, right: 20 },  // Chat icon position (px)
              // onSessionUpdate: (id) => console.log('Session:', id),
              // onLocaleUpdate: (locale) => console.log('Locale:', locale),
            });
            mcbs.showIcon();
          };
          document.head.appendChild(script);
          this.scriptEl = script;
        },
        removeScript(): void {
          if(this.scriptEl) {
            document.head.removeChild(this.scriptEl);
            this.scriptEl = null;
          }
        },
      },
    });
    </script>
  3. Use the component in App.vue

    Import and render MaisonChat near the root of your app and pass your Client UUID.

    vue
    <!-- App.vue -->
    <template>
      <MaisonChat client-id="YOUR_CLIENT_UUID"/>
      <!-- rest of your app -->
    </template>
    
    <script setup lang="ts">
    import MaisonChat from './components/MaisonChat.vue';
    </script>
  4. Verify in DevTools

    Open your browser DevTools, switch to the Elements tab, and look for #maison-chat-icon in the DOM. If it's present, the widget initialised correctly.

For the full list of configuration options, control methods, and callbacks, see the Widget SDK Reference.

Common mistakes
Calling initMcbs outside onload. initMcbs is not available until the bundle finishes loading. Always call it inside script.onload.
Nuxt or SSR environments. window does not exist on the server. onMounted and mounted are browser-only hooks and are safe. For Nuxt, wrap the component in <ClientOnly> if needed.
Keep a single instance. Render MaisonChat once at the root of your app. Mounting it multiple times will inject multiple script tags.
Using string values for iconPosition. Write bottom: 20, not bottom: '20px'. Values are plain numbers representing pixels — do not include units.

Not sure if it's set up correctly? Use the Site Diagnostic tool.