Files
librechat.ai/app/layout.tsx
Danny Avila c0550152a7 chore: add Scarf pixel tracking script to RootLayout (#520)
* chore: add Scarf pixel tracking script to RootLayout

- Integrated a conditional Scarf pixel tracking script in the RootLayout component to monitor user interactions and page views.
- The script is loaded after the interactive phase and utilizes the NEXT_PUBLIC_SCARF_PIXEL_ID environment variable for configuration.

* refactor: reorder imports in layout.tsx for consistency

- Adjusted the order of imports in the layout.tsx file to maintain a consistent structure, placing the Provider import after the Script import for better readability.
2026-03-02 16:10:15 -05:00

78 lines
2.5 KiB
TypeScript

import Script from 'next/script'
import { GeistSans } from 'geist/font/sans'
import { GeistMono } from 'geist/font/mono'
import { Analytics } from '@vercel/analytics/react'
import { SpeedInsights } from '@vercel/speed-insights/next'
import type { ReactNode } from 'react'
import type { Metadata } from 'next'
import { Provider } from '@/components/provider'
import './global.css'
export const metadata: Metadata = {
title: {
default: 'LibreChat',
template: '%s | LibreChat',
},
description: 'The Open-Source AI Platform',
metadataBase: new URL('https://www.librechat.ai'),
openGraph: {
type: 'website',
locale: 'en_US',
siteName: 'LibreChat',
images: ['/images/socialcards/default-image.png'],
},
twitter: {
card: 'summary_large_image',
},
icons: {
icon: [
{ url: '/favicon-32x32.png', sizes: '32x32', type: 'image/png' },
{ url: '/favicon-16x16.png', sizes: '16x16', type: 'image/png' },
],
apple: '/apple-touch-icon.png',
},
}
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html
lang="en"
className={`${GeistSans.variable} ${GeistMono.variable}`}
suppressHydrationWarning
>
<body className="flex min-h-screen flex-col">
<Provider>{children}</Provider>
<Analytics />
<SpeedInsights />
{process.env.NEXT_PUBLIC_SCARF_PIXEL_ID && (
<Script
id="scarf-pixel"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
(function () {
var PIXEL_ID = '${process.env.NEXT_PUBLIC_SCARF_PIXEL_ID}';
function sendScarfPing() {
var img = new Image();
img.referrerPolicy = 'no-referrer-when-downgrade';
img.src = 'https://static.scarf.sh/a.png?x-pxid=' + PIXEL_ID;
}
var originalPushState = history.pushState;
history.pushState = function () {
originalPushState.apply(this, arguments);
window.dispatchEvent(new Event('scarf:locationchange'));
};
window.addEventListener('hashchange', sendScarfPing);
window.addEventListener('popstate', sendScarfPing);
window.addEventListener('scarf:locationchange', sendScarfPing);
sendScarfPing();
})();
`,
}}
/>
)}
</body>
</html>
)
}