NIKHIL KUMAR
NIKHIL KUMAR

Reputation: 1

building widget --> works locally and when deployed on vercel -->how can i make this an embeddable script to be embedded in html websites?

Have built a chat widget using Next.js and webpack, it works, locally and when deployed on Vercel. Sharing the github repo for reference - https://github.com/SuprSend-NotificationAPI/bifrost

Also sharing the native vercel deployment where you can see it working --https://bifrost-dun.vercel.app/test-widget

The issue is I'm unable to get this created in a script to be embeddable in external HTML websites.

This is the script code I'm trying to use:

 <script src="https://bifrost-dun.vercel.app/_next/static/chunks/widget-bundle.js"></script> <script>   console.log('Script loaded, BifrostChatWidget:', initializeBifrostChatWidget);   if (typeof initializeBifrostChatWidget !== 'undefined' && typeof initializeBifrostChatWidget.attachToWindow === 'function') {     initializeBifrostChatWidget.attachToWindow();   }    window.onload = function() {     console.log('Window loaded, checking for initializeBifrostChatWidget');     if (typeof window.initializeBifrostChatWidget === 'function') {       window.initializeBifrostChatWidget();     } else {       console.error('Bifrost Chat Widget initialization function not found');     }   }; </script> 


console erro
r

  1. What am I doing wrong?

  2. What should be the approach or any reference material?

Also attaching two relevant code blocks from this project.

widget-entry.tsx

import React from 'react';
import { createRoot } from 'react-dom/client';
import ChatWidget from './components/ChatWidget';

console.log('Widget entry file loaded');

function initializeBifrostChatWidget() {
  console.log('Initializing Bifrost Chat Widget');
  const container = document.createElement('div');
  container.id = 'bifrost-chat-widget';
  document.body.appendChild(container);

  const root = createRoot(container);
  root.render(React.createElement(ChatWidget));
  console.log('Bifrost Chat Widget initialized');
}

if (typeof window !== 'undefined') {
  (window as any).initializeBifrostChatWidget = initializeBifrostChatWidget;
  console.log('initializeBifrostChatWidget function attached to window:', (window as any).initializeBifrostChatWidget);
}

// For tree-shaking prevention, export the function
export { initializeBifrostChatWidget };

console.log('Widget entry file execution completed');

and next.config.js

const path = require('path');

/** @type {import('next').NextConfig} */
const nextConfig = (phase, { defaultConfig }) => {
  const BUILD_ID = Date.now();

  return {
    reactStrictMode: true,
    images: {
      remotePatterns: [
        {
          protocol: 'https',
          hostname: 'cdn-b.saashub.com',
          pathname: '/**',
        },
        {
          protocol: 'https',
          hostname: 'randomuser.me',
          pathname: '/**',
        },
      ],
    },

    webpack: (config, { isServer, webpack }) => {
      // Use different devtool configurations for development and production
      config.devtool = isServer
        ? false
        : phase === 'phase-production-build'
        ? 'source-map'
        : 'eval-source-map';

      if (!isServer) {
        // Minification/Optimization - Disable certain optimizations for debugging
        config.optimization.minimize = true; // Disable minification for debugging
        config.optimization.concatenateModules = true; // Disable module concatenation
        config.optimization.splitChunks = false; // Disable code splitting

        // Ensure the widget entry point is included
        const originalEntry = config.entry;
        config.entry = async () => {
          const entries = await originalEntry();
          if (!entries['widget-bundle']) {
            entries['widget-bundle'] = {
              import: path.resolve(__dirname, 'widget-entry.tsx'),
              dependOn: undefined,
            };
          }
          return entries;
        };

        // Output Configuration - Expose the widget as a global library
        config.output.library = {
          name: 'BifrostChatWidget',
          type: 'umd', // or 'umd' for broader compatibility
        };

        // Ensure the split chunks configuration is set up (only if you re-enable splitChunks)
        if (config.optimization.splitChunks !== false) {
          config.optimization.splitChunks.cacheGroups = {
            ...config.optimization.splitChunks.cacheGroups,
            widgetBundle: {
              name: 'widget-bundle',
              test: /widget-entry\.tsx$/,
              chunks: 'all',
              enforce: true,
            },
          };
        }

        // Filename/Path - Ensure correct file naming for cache busting
        config.output.filename = (pathData) => {
          return pathData.chunk.name === 'widget-bundle'
            ? `static/chunks/[name].js?v=${BUILD_ID}`
            : `static/chunks/[name].[contenthash].js`;
        };

        config.output.chunkFilename = (pathData) => {
          return pathData.chunk.name === 'widget-bundle'
            ? `static/chunks/[name].js?v=${BUILD_ID}`
            : `static/chunks/[name].[contenthash].js`;
        };
      }

      return config;
    },
  };
};

module.exports = nextConfig;

Thanks

  1. TUsed script tag to load the widget as shown in code.

  2. Created a standalone script that initializes the widget and provides a public API, didn't work still.

Upvotes: 0

Views: 35

Answers (0)

Related Questions