Skip to content

Component & Bridge Integration

Integrating Bagisto Native into your storefront involves two distinct layers that convert your web application into a native-ready powerhouse.

What you will do in this section:

  1. UI Component Anchors: Direct React components from @bagisto-native/react that act as native UI hooks.
  2. Functional Bridges: Event-driven logic that handles bidirectional communication.
  3. Performance Optimization: Implementation of lazy-loading for native-specific bridge components.

1. UI Component Integration

These components are required for the native bridge to identify "hooks" in your web page. They are rendered as hidden elements that the native app detects.

Pattern: Lazy-Loaded Wrappers

Since these components depend on the browser window and DOM, they must be client-only.

tsx
// src/components/native/HotwireSearch.tsx
'use client';
import dynamic from "next/dynamic";

const HotwireSearch = dynamic(
    () => import('@bagisto-native/react').then(mod => mod.HotwireSearch),
    { ssr: false }
);

export default function NativeSearch() {
    return <HotwireSearch />;
}

2. Functional Bridge Integration

Bridges are "headless" components (returning null) that synchronize state or handle routing.

A. Routing Bridge (Native → Web)

Used to handle events triggered by native actions, such as a Barcode Scan or an Image Search result.

tsx
// src/components/hotwire/TurboSearchRouterBridge.tsx
'use client';
import { useEffect } from "react";
import { useRouter } from "next/navigation"; 

export default function TurboSearchRouterBridge() {
  const router = useRouter();

  useEffect(() => {
    const handleTurboSearch = (e: Event) => {
      const customEvent = e as CustomEvent<{ query?: string; code?: string }>;
      const query = customEvent.detail.query || customEvent.detail.code;
      
      if (query) {
        router.push(`/search?q=${encodeURIComponent(query)}`);
      }
    };

    window.addEventListener("turbo:next-search", handleTurboSearch);
    return () => window.removeEventListener("turbo:next-search", handleTurboSearch);
  }, [router]);

  return null; 
}

B. UI Synchronization Bridge (Native ↔ Web)

Used when the native application needs to trigger a web-based UI element, like opening the Cart Slide-over from a native button.

tsx
// src/components/hotwire/TurboCartModalBridge.tsx
'use client';
import { useEffect } from "react";
import { triggerDynamicButtonModalOpenEvent } from "@bagisto-native/core";

export default function TurboCartModalBridge({ onOpen, isOpen }) {
  useEffect(() => {
    const handleTurboCartModalOpen = (e: Event) => {
      if (!isOpen) {
        triggerDynamicButtonModalOpenEvent(); // Sync state to bridge
        onOpen(); // Open web cart modal
      }
    };

    window.addEventListener("turbo:next-cart-modal", handleTurboCartModalOpen);
    return () => window.removeEventListener("turbo:next-cart-modal", handleTurboCartModalOpen);
  }, [isOpen, onOpen]);

  return null;
}

C. State Synchronization (Web → Native)

Used to inform the native app of web-side changes, such as Navigation/History changes or updating a Cart Count Badge.

tsx
// src/components/hotwire/HistorySync.tsx
'use client';
import { useEffect } from "react";
import { usePathname, useSearchParams } from "next/navigation";

export default function HistorySync() {
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    const url = new URL(`${window.location.origin}${pathname}${searchParams.toString() ? `?${searchParams.toString()}` : ""}`);
    const seoTitle = document?.title || "";

    const bridgeElement = document.querySelector("[data-controller='bridge--historysync']");

    if (bridgeElement) {
      bridgeElement.dispatchEvent(new CustomEvent("turbo:next-history-sync", {
        detail: { url, seoTitle },
        bubbles: true
      }));
    }
  }, [pathname, searchParams]);

  return null;
}

3. Global Orchestration

In your RootLayout, you should mount these bridges alongside the bridge script to ensure consistent behavior across all pages.

tsx
// src/app/layout.tsx
import Script from "next/script";
import TurboSearchRouterBridge from "@/components/hotwire/TurboSearchRouterBridge";
import HistorySync from "@/components/hotwire/HistorySync";
import NativeToast from "@/components/native/HotwireToast";

export default function RootLayout({ children }) {
  return (
    <html>
      <head>
        <Script strategy="beforeInteractive" src="/hotwire/bundle.js" />
      </head>
      <body>
        {children}
        
        {/* Logic Bridges */}
        <TurboSearchRouterBridge />
        <HistorySync />
        
        {/* UI Anchors */}
        <NativeToast />
      </body>
    </html>
  );
}

4. Summary of Bridge Patterns

PatternInteraction DirectionReal-world Example
Routing BridgeNative → WebRedirecting web app after a Barcode Scan.
Logic BridgeNative → WebApp shell button opening the web Cart modal.
Sync BridgeWeb → NativeUpdating the App's Title Bar on navigation.
UI ComponentsNative ↔ WebTriggering native "Image Search" from a web button.

By combining React Wrappers with Functional Bridges, you create a truly integrated experience where the web and native layers behave as a single, fluid application.

Released under the MIT License.