Loupe

Quick Start

Add the Loupe feedback widget to any web app in under a minute. Pick your framework below — every snippet is copy-paste ready against @loupeink/web-sdk.

Plain HTML

No build step. Drop the script tag before </body> and initialize with your project API key. The widget mounts itself.

<script src="https://cdn.jsdelivr.net/npm/@loupeink/web-sdk/dist/index.global.js"></script>
<script>
  Loupe.init({ apiKey: "lp_your_project_api_key" });
</script>

Now, get your project API key

The snippet above uses a placeholder. To go live, sign in at app.loupe.ink, open Organization Settings → API Keys, pick a project and click Generate. Copy the lp_… key — it is shown only once — and replace lp_your_project_api_key.

React

Install the package, then call init once at your app root. Return destroy() from the effect so the widget cleans up correctly (including React StrictMode double-mounts).

Install
npm install @loupeink/web-sdk
App root
import { useEffect } from "react";
import { init, destroy } from "@loupeink/web-sdk";

export function App() {
  useEffect(() => {
    init({ apiKey: import.meta.env.VITE_LOUPE_API_KEY });
    return () => destroy();
  }, []);

  return <YourApp />;
}

Next.js

The SDK runs in the browser, so it must live in a client component. App Router and Pages Router differ only in where you mount it.

Install
npm install @loupeink/web-sdk
App Router — app/loupe.tsx
"use client";
import { useEffect } from "react";
import { init, destroy } from "@loupeink/web-sdk";

export function Loupe() {
  useEffect(() => {
    init({ apiKey: process.env.NEXT_PUBLIC_LOUPE_API_KEY! });
    return () => destroy();
  }, []);
  return null;
}
App Router — app/layout.tsx
import { Loupe } from "./loupe";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Loupe />
      </body>
    </html>
  );
}
Pages Router — pages/_app.tsx
import { useEffect } from "react";
import { init, destroy } from "@loupeink/web-sdk";
import type { AppProps } from "next/app";

export default function App({ Component, pageProps }: AppProps) {
  useEffect(() => {
    init({ apiKey: process.env.NEXT_PUBLIC_LOUPE_API_KEY! });
    return () => destroy();
  }, []);

  return <Component {...pageProps} />;
}

Vue

Install the package and initialize from a root component's onMounted hook. Clean up with destroy() in onUnmounted.

Install
npm install @loupeink/web-sdk
App.vue
<script setup lang="ts">
import { onMounted, onUnmounted } from "vue";
import { init, destroy } from "@loupeink/web-sdk";

onMounted(() => init({ apiKey: import.meta.env.VITE_LOUPE_API_KEY }));
onUnmounted(() => destroy());
</script>

Need more options — themes, positioning, callbacks, self-hosted endpoints?

Full SDK reference on GitHub →