Next.js & custom code
Integrate the Lapis pixel in a Next.js, React, or custom stack: layout script, click-ID persistence, and server events.
When you own the code, the integration is three small pieces: the script in your root layout, the click ID persisted wherever your conversion flow starts, and a server event from your backend.
1. Pixel in the root layout
Next.js App Router:
// app/layout.tsx
import Script from "next/script";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://cdn.trylapis.com/pixel/v1/lapis-pixel.js"
data-key="pk_YOUR_PIXEL_KEY"
strategy="afterInteractive"
/>
</body>
</html>
);
}
For any other stack, use the plain <script> tag before </body> in your base template.
2. Persist the click ID
Here is a small helper, used at the start of your conversion flow (the signup mutation, or checkout-session creation):
export function getLapisClickId(): string | null {
if (typeof document === "undefined") return null;
const cookie = document.cookie
.split("; ")
.find((entry) => entry.startsWith("lapis_sid="));
if (!cookie) return null;
try {
const session = JSON.parse(
decodeURIComponent(cookie.split("=").slice(1).join("="))
);
return session.sid ?? null;
} catch {
return null;
}
}
Store the value on the business record that will eventually convert:
- Signup: include it in the signup mutation, writing to
accounts.lapis_click_id(a nullable column). - Checkout: put it in the checkout session metadata (for example, Stripe
metadata.lapis_click_id) and read it back in the payment webhook. - Cross-domain (marketing site to app): pass it through the URL. See the decorator script in the Framer guide.
null is a valid value, because most traffic is organic. Never fabricate an ID.
3. Send the conversion from your backend
export async function sendLapisConversionEvent(event: {
event_id: string;
event_type: string;
lapis_click_id: string | null;
value?: number;
currency?: string;
external_user_id?: string;
properties?: Record<string, unknown>;
}) {
if (!event.lapis_click_id) return; // organic, so skip
const response = await fetch(
"https://api.trylapis.com/api/v1/pixel/server-events",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LAPIS_SERVER_EVENT_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(event),
}
);
return response.json();
}
Call it the moment the conversion is confirmed: the payment webhook, the post-verification signup handler, or the CRM sync. For the field reference and response codes, see the Conversion API. For which events to send, see the event guides.
Stack notes
- Server components / SSR: the cookie is first-party and readable on the server too (
cookies().get("lapis_sid")in Next.js). Parsing it on the server during signup avoids a client round-trip. - SPAs: the pixel records the landing page view automatically. Client-side route changes need no manual tracking for attribution to work, because the session already exists.
- Browser conversions (optional, for example instant-feedback events):
if (window.lapis) lapis("lead", { value: 50 }). See Lapis Pixel. - CSP: allow
https://cdn.trylapis.com(script-src) andhttps://api.trylapis.com(connect-src).