Saltar al contenido

Documentación

Guías por framework

Dónde inicializar Wutbug en Next.js, React, Vue, Nuxt, SvelteKit, Angular y sitios renderizados en el servidor.

La regla es la misma en todos los frameworks: llama a wutbug.init() una sola vez, en el navegador, lo antes posible. Nunca durante el renderizado en el servidor: init() lanza un error si no hay window.

En los ejemplos, la key y el endpoint vienen de variables de entorno públicas. Usa la convención de tu framework (NEXT_PUBLIC_*, VITE_*, PUBLIC_*, NUXT_PUBLIC_*).

Next.js (App Router)

Crea un componente cliente que inicialice en useEffect y móntalo en el layout raíz.

app/wutbug.tsx
'use client';

import { useEffect } from 'react';
import wutbug from '@wutbug/sdk';

export function Wutbug() {
  useEffect(() => {
    wutbug.init({
      projectKey: process.env.NEXT_PUBLIC_WUTBUG_KEY!,
      endpoint: process.env.NEXT_PUBLIC_WUTBUG_ENDPOINT,
      release: process.env.NEXT_PUBLIC_RELEASE, // opcional
    });
    return () => wutbug.destroy();
  }, []);
  return null;
}
app/layout.tsx
import { Wutbug } from './wutbug';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="es">
      <body>
        {children}
        <Wutbug />
      </body>
    </html>
  );
}

El return () => wutbug.destroy() evita instancias duplicadas con el doble montaje de React en desarrollo. De todos modos, llamar a init() de nuevo reemplaza la instancia anterior.

Para identificar al usuario, llama a wutbug.setUser() desde el componente cliente que ya conoce la sesión:

TSX
'use client';
import { useEffect } from 'react';
import wutbug from '@wutbug/sdk';

export function IdentifyUser({ user }: { user: { id: string; email: string; name: string } | null }) {
  useEffect(() => {
    wutbug.setUser(user);
  }, [user]);
  return null;
}

Next.js (Pages Router)

pages/_app.tsx
import type { AppProps } from 'next/app';
import { useEffect } from 'react';
import wutbug from '@wutbug/sdk';

export default function App({ Component, pageProps }: AppProps) {
  useEffect(() => {
    wutbug.init({
      projectKey: process.env.NEXT_PUBLIC_WUTBUG_KEY!,
      endpoint: process.env.NEXT_PUBLIC_WUTBUG_ENDPOINT,
    });
  }, []);
  return <Component {...pageProps} />;
}

React con Vite (o cualquier SPA)

En una SPA no hay renderizado en el servidor: inicializa en el entry, antes de montar la app.

src/main.tsx
import { createRoot } from 'react-dom/client';
import wutbug from '@wutbug/sdk';
import App from './App';

wutbug.init({
  projectKey: import.meta.env.VITE_WUTBUG_KEY,
  endpoint: import.meta.env.VITE_WUTBUG_ENDPOINT,
  release: import.meta.env.VITE_RELEASE,
});

createRoot(document.getElementById('root')!).render(<App />);

Para abrir el formulario desde un error boundary:

TSX
import wutbug from '@wutbug/sdk';

function ErrorFallback({ error }: { error: Error }) {
  return (
    <div role="alert">
      <p>Algo salió mal.</p>
      <button onClick={() => wutbug.open({ description: `Error: ${error.message}` })}>Reportar el problema</button>
    </div>
  );
}

Vue 3 (Vite)

src/main.ts
import { createApp } from 'vue';
import wutbug from '@wutbug/sdk';
import App from './App.vue';

wutbug.init({
  projectKey: import.meta.env.VITE_WUTBUG_KEY,
  endpoint: import.meta.env.VITE_WUTBUG_ENDPOINT,
});

createApp(App).mount('#app');

Nuxt 3 / 4

Un plugin con sufijo .client solo corre en el navegador.

plugins/wutbug.client.ts
import wutbug from '@wutbug/sdk';

export default defineNuxtPlugin(() => {
  const config = useRuntimeConfig();
  wutbug.init({
    projectKey: config.public.wutbugKey as string,
    endpoint: config.public.wutbugEndpoint as string,
  });
});
nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    public: { wutbugKey: '', wutbugEndpoint: '' }, // NUXT_PUBLIC_WUTBUG_KEY, NUXT_PUBLIC_WUTBUG_ENDPOINT
  },
});

SvelteKit

Inicializa en onMount del layout raíz (solo corre en el navegador).

src/routes/+layout.svelte
<script lang="ts">
  import { onMount } from 'svelte';
  import wutbug from '@wutbug/sdk';
  import { PUBLIC_WUTBUG_KEY, PUBLIC_WUTBUG_ENDPOINT } from '$env/static/public';

  let { children } = $props();

  onMount(() => {
    wutbug.init({ projectKey: PUBLIC_WUTBUG_KEY, endpoint: PUBLIC_WUTBUG_ENDPOINT });
    return () => wutbug.destroy();
  });
</script>

{@render children()}

Angular

Inicializa en main.ts antes de arrancar la app. Si usas SSR, protege la llamada con isPlatformBrowser o hazla en un afterNextRender.

src/main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import wutbug from '@wutbug/sdk';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
import { environment } from './environments/environment';

wutbug.init({ projectKey: environment.wutbugKey, endpoint: environment.wutbugEndpoint });

bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));

HTML, WordPress, Rails, Django, Laravel y PHP

En sitios renderizados en el servidor, usa la etiqueta script en el <head> de la plantilla base. Si la persona inició sesión, identifícala en la misma plantilla con la cola de llamadas.

HTML
<script>
  window.wutbug = window.wutbug || { q: [] };
  ['init', 'setUser', 'setMetadata', 'open', 'close'].forEach(function (m) {
    window.wutbug[m] = window.wutbug[m] || function () { window.wutbug.q.push([m, arguments]); };
  });
</script>
<script type="module" src="https://cdn.wutbug.dev/v0/wutbug.js"
  data-project-key="pk_tu_key" data-endpoint="https://api.wutbug.dev"></script>

WordPress

Agrega el script con wp_head en el functions.php de tu tema hijo (o con un plugin de fragmentos de código):

functions.php
add_action('wp_head', function () {
  echo '<script type="module" src="https://cdn.wutbug.dev/v0/wutbug.js" data-project-key="pk_tu_key" data-endpoint="https://api.wutbug.dev"></script>';
});

Rails

app/views/layouts/application.html.erb
<script type="module" src="https://cdn.wutbug.dev/v0/wutbug.js"
  data-project-key="<%= ENV['WUTBUG_KEY'] %>" data-endpoint="<%= ENV['WUTBUG_ENDPOINT'] %>"></script>
<% if current_user %>
  <script>
    window.wutbug = window.wutbug || { q: [] };
    (window.wutbug.setUser || function () { window.wutbug.q.push(['setUser', arguments]); })(
      <%= raw({ id: current_user.id.to_s, email: current_user.email }.to_json) %>
    );
  </script>
<% end %>

Django

templates/base.html
<script type="module" src="https://cdn.wutbug.dev/v0/wutbug.js"
  data-project-key="{{ WUTBUG_KEY }}" data-endpoint="{{ WUTBUG_ENDPOINT }}"></script>

Laravel (Blade)

resources/views/layouts/app.blade.php
<script type="module" src="https://cdn.wutbug.dev/v0/wutbug.js"
  data-project-key="{{ config('services.wutbug.key') }}" data-endpoint="{{ config('services.wutbug.endpoint') }}"></script>

Content Security Policy

Si tu sitio usa CSP, permite:

  • script-src: el dominio del CDN (solo si usas la etiqueta script).
  • connect-src: el endpoint de la API y el almacenamiento de objetos al que se suben los archivos del reporte (las URLs firmadas que entrega la API).
  • img-src: data: y blob: (captura de pantalla).