Next.js (App Router)
Como window solo está disponible en el cliente, el cargador del widget debe ser un Client Component montado en el layout raíz.
El componente cargador
components/WidgetLoader.tsx
// components/WidgetLoader.tsx
'use client';
import { useEffect } from 'react';
import { useSession } from 'next-auth/react';
export function WidgetLoader() {
const { data: session } = useSession();
useEffect(() => {
if (window.self !== window.top) return; // skip inside iframes
if (document.getElementById('ph-embed-script')) return;
if (!(window as any).PH) {
(window as any).PH = function(...args: unknown[]) {
((window as any).PH.q = (window as any).PH.q ?? []).push(args);
};
}
(window as any).PH('init', { widgetId: process.env.NEXT_PUBLIC_PH_WIDGET_ID });
const script = document.createElement('script');
script.id = 'ph-embed-script';
script.src = '/embed.js'; // if self-hosting, or the CDN URL
script.async = true;
document.head.appendChild(script);
}, []);
useEffect(() => {
const user = session?.user;
if (user?.email) {
(window as any).PH?.('identify', {
id: user.id ?? user.email,
email: user.email,
name: user.name ?? undefined,
});
} else {
(window as any).PH?.('unidentify');
}
}, [session]);
return null;
}app/layout.tsx
// app/layout.tsx
import { WidgetLoader } from '@/components/WidgetLoader';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<WidgetLoader />
</body>
</html>
);
}ℹ
Si alojas el widget tú mismo (es decir, Priority Hunter es tu propio despliegue), establece src como /embed.js para servirlo desde el mismo origen y evitar problemas de CORS o CSP.
Siguiente: Vue 3 →
Documentación de Priority Hunterpriorityhunter.com →