feat: trim integration pages from 75 to 25 + migrate to MDX

- Add dedicatedPage flag to integration registry (25 true, 50 false)
- Delete hardcoded nextjs/react/vue/wordpress route pages (wrong metadata)
- Hub page routes non-dedicated integrations to /integrations/script-tag
- Add 301 redirects for 50 removed slugs → /integrations/script-tag
- Migrate guide content from TSX to MDX (content/integrations/*.mdx)
- Add gray-matter, next-mdx-remote, remark-gfm dependencies
- Add content loader (lib/integration-content.ts) matching ciphera-website pattern
- Add prebuild script for integration guide index generation
- Sitemap reduced from 83 to 35 URLs with real lastmod dates
- Remove seoDescription from registry (now in MDX frontmatter)
This commit is contained in:
Usman Baig
2026-03-29 00:28:47 +01:00
parent 20d7bdd482
commit 066f1288f1
42 changed files with 2515 additions and 4233 deletions

3
.gitignore vendored
View File

@@ -1,5 +1,8 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# auto-generated
/lib/integration-guides.gen.ts
# dependencies # dependencies
/node_modules /node_modules
/.pnp /.pnp

View File

@@ -1,46 +1,56 @@
/** /**
* @file Dynamic route for individual integration guide pages. * @file Dynamic route for individual integration guide pages.
* *
* Handles all 50 integration routes via [slug]. * Renders MDX content from content/integrations/*.mdx via next-mdx-remote.
* Exports generateStaticParams for static generation and * Exports generateStaticParams for static generation and
* generateMetadata for per-page SEO (title, description, OG, JSON-LD). * generateMetadata for per-page SEO (title, description, OG, JSON-LD).
*/ */
import type { Metadata } from 'next' import type { Metadata } from 'next'
import { notFound } from 'next/navigation' import { notFound } from 'next/navigation'
import { MDXRemote } from 'next-mdx-remote/rsc'
import remarkGfm from 'remark-gfm'
import { CodeBlock } from '@ciphera-net/ui'
import { integrations, getIntegration } from '@/lib/integrations' import { integrations, getIntegration } from '@/lib/integrations'
import { getGuideContent } from '@/lib/integration-guides' import { getIntegrationGuide } from '@/lib/integration-content'
import { IntegrationGuide } from '@/components/IntegrationGuide' import { IntegrationGuide } from '@/components/IntegrationGuide'
// * ─── Static Params ─────────────────────────────────────────────── // * ─── MDX Components ────────────────────────────────────────────
export function generateStaticParams() { const mdxComponents = {
return integrations.map((i) => ({ slug: i.id })) CodeBlock,
} }
// * ─── SEO Metadata ──────────────────────────────────────────────── // * ─── Static Params ─────────────────────────────────────────────
export function generateStaticParams() {
return integrations
.filter((i) => i.dedicatedPage)
.map((i) => ({ slug: i.id }))
}
// * ─── SEO Metadata ──────────────────────────────────────────────
interface PageProps { interface PageProps {
params: Promise<{ slug: string }> params: Promise<{ slug: string }>
} }
export async function generateMetadata({ params }: PageProps): Promise<Metadata> { export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { slug } = await params const { slug } = await params
const integration = getIntegration(slug) const guide = getIntegrationGuide(slug)
if (!integration) return {} if (!guide) return {}
const title = `How to Add Pulse Analytics to ${integration.name} | Pulse by Ciphera` const title = `How to Add Pulse Analytics to ${guide.title} | Pulse by Ciphera`
const description = integration.seoDescription const description = guide.description
const url = `https://pulse.ciphera.net/integrations/${integration.id}` const url = `https://pulse.ciphera.net/integrations/${guide.slug}`
return { return {
title, title,
description, description,
keywords: [ keywords: [
`${integration.name} analytics`, `${guide.title} analytics`,
`${integration.name} Pulse`, `${guide.title} Pulse`,
'privacy-first analytics', 'privacy-first analytics',
'website analytics', 'website analytics',
'Ciphera Pulse', 'Ciphera Pulse',
integration.name, guide.title,
], ],
alternates: { canonical: url }, alternates: { canonical: url },
openGraph: { openGraph: {
@@ -58,21 +68,19 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
} }
} }
// * ─── Page Component ────────────────────────────────────────────── // * ─── Page Component ────────────────────────────────────────────
export default async function IntegrationPage({ params }: PageProps) { export default async function IntegrationPage({ params }: PageProps) {
const { slug } = await params const { slug } = await params
const integration = getIntegration(slug) const integration = getIntegration(slug)
if (!integration) return notFound() const guide = getIntegrationGuide(slug)
if (!integration || !guide) return notFound()
const content = getGuideContent(slug)
if (!content) return notFound()
// * HowTo JSON-LD for rich search snippets // * HowTo JSON-LD for rich search snippets
const jsonLd = { const jsonLd = {
'@context': 'https://schema.org', '@context': 'https://schema.org',
'@type': 'HowTo', '@type': 'HowTo',
name: `How to Add Pulse Analytics to ${integration.name}`, name: `How to Add Pulse Analytics to ${integration.name}`,
description: integration.seoDescription, description: guide.description,
step: [ step: [
{ {
'@type': 'HowToStep', '@type': 'HowToStep',
@@ -104,7 +112,11 @@ export default async function IntegrationPage({ params }: PageProps) {
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/> />
<IntegrationGuide integration={integration}> <IntegrationGuide integration={integration}>
{content} <MDXRemote
source={guide.content}
components={mdxComponents}
options={{ mdxOptions: { remarkPlugins: [remarkGfm] } }}
/>
</IntegrationGuide> </IntegrationGuide>
</> </>
) )

View File

@@ -1,128 +0,0 @@
'use client'
import Link from 'next/link'
import { ArrowLeftIcon } from '@ciphera-net/ui'
export default function NextJsIntegrationPage() {
return (
<div className="relative min-h-screen flex flex-col overflow-hidden">
{/* * --- ATMOSPHERE (Background) --- */}
<div className="absolute inset-0 -z-10 pointer-events-none">
<div className="absolute bottom-0 right-1/4 w-[500px] h-[500px] bg-neutral-400/10 rounded-full blur-[128px] opacity-40" />
<div
className="absolute inset-0 bg-grid-pattern opacity-[0.05]"
style={{ maskImage: 'radial-gradient(ellipse at center, black 0%, transparent 70%)' }}
/>
</div>
<div className="flex-grow w-full max-w-4xl mx-auto px-4 pt-20 pb-10 z-10">
<Link
href="/integrations"
className="inline-flex items-center text-sm text-neutral-500 hover:text-brand-orange mb-8 transition-colors"
>
<ArrowLeftIcon className="w-4 h-4 mr-2" />
Back to Integrations
</Link>
<div className="flex items-center gap-4 mb-8">
<div className="p-3 bg-neutral-800 rounded-xl">
<svg viewBox="0 0 128 128" className="w-10 h-10 invert">
<path d="M64 0C28.7 0 0 28.7 0 64s28.7 64 64 64 64-28.7 64-64S99.3 0 64 0zm27.6 93.9c-.8.9-2.2 1-3.1.2L42.8 52.8V88c0 1.3-1.1 2.3-2.3 2.3h-7.4c-1.3 0-2.3-1.1-2.3-2.3V40c0-1.3 1.1-2.3 2.3-2.3h7.4c1 0 1.9.6 2.2 1.5l48.6 44.8V40c0-1.3 1.1-2.3 2.3-2.3h7.4c1.3 0 2.3 1.1 2.3 2.3v48c0 1.3-1.1 2.3-2.3 2.3h-6.8c-.9 0-1.7-.5-2.1-1.3z" />
</svg>
</div>
<h1 className="text-4xl md:text-5xl font-bold tracking-tight text-white">
Next.js Integration
</h1>
</div>
<div className="prose prose-invert max-w-none">
<p className="lead text-xl text-neutral-400">
The best way to add Pulse to your Next.js application is using the built-in <code>next/script</code> component.
</p>
<hr className="my-8 border-neutral-800" />
<h3>Using App Router (Recommended)</h3>
<p>
Add the script to your root layout file (usually <code>app/layout.tsx</code> or <code>app/layout.js</code>).
</p>
<div className="bg-neutral-900 rounded-xl overflow-hidden border border-neutral-800 my-6">
<div className="flex items-center px-4 py-2 bg-neutral-800 border-b border-neutral-800">
<span className="text-xs text-neutral-400 font-mono">app/layout.tsx</span>
</div>
<div className="p-4 overflow-x-auto">
<pre className="text-sm font-mono text-neutral-300">
{`import Script from 'next/script'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<Script
defer
src="https://pulse.ciphera.net/script.js"
data-domain="your-site.com"
strategy="afterInteractive"
/>
</head>
<body>{children}</body>
</html>
)
}`}
</pre>
</div>
</div>
<h3>Using Pages Router</h3>
<p>
If you are using the older Pages Router, add the script to your custom <code>_app.tsx</code> or <code>_document.tsx</code>.
</p>
<div className="bg-neutral-900 rounded-xl overflow-hidden border border-neutral-800 my-6">
<div className="flex items-center px-4 py-2 bg-neutral-800 border-b border-neutral-800">
<span className="text-xs text-neutral-400 font-mono">pages/_app.tsx</span>
</div>
<div className="p-4 overflow-x-auto">
<pre className="text-sm font-mono text-neutral-300">
{`import Script from 'next/script'
import type { AppProps } from 'next/app'
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Script
defer
src="https://pulse.ciphera.net/script.js"
data-domain="your-site.com"
strategy="afterInteractive"
/>
<Component {...pageProps} />
</>
)
}`}
</pre>
</div>
</div>
<h3>Configuration Options</h3>
<ul>
<li>
<strong>data-domain</strong>: The domain name you added to your Pulse dashboard (e.g., <code>example.com</code>).
</li>
<li>
<strong>src</strong>: The URL of our tracking script: <code>https://pulse.ciphera.net/script.js</code>
</li>
<li>
<strong>strategy</strong>: We recommend <code>afterInteractive</code> to ensure it loads quickly without blocking hydration.
</li>
</ul>
</div>
</div>
</div>
)
}

View File

@@ -244,7 +244,7 @@ export default function IntegrationsPage() {
transition={{ duration: 0.4, delay: i * 0.05 }} transition={{ duration: 0.4, delay: i * 0.05 }}
> >
<Link <Link
href={`/integrations/${integration!.id}`} href={integration!.dedicatedPage ? `/integrations/${integration!.id}` : '/integrations/script-tag'}
className="group flex items-center gap-3 p-4 bg-neutral-900/50 backdrop-blur-sm border border-neutral-800 rounded-xl hover:border-brand-orange/50 transition-all duration-300 hover:-translate-y-0.5 hover:shadow-lg h-full" className="group flex items-center gap-3 p-4 bg-neutral-900/50 backdrop-blur-sm border border-neutral-800 rounded-xl hover:border-brand-orange/50 transition-all duration-300 hover:-translate-y-0.5 hover:shadow-lg h-full"
> >
<div className="p-2 bg-neutral-800 rounded-lg shrink-0 group-hover:scale-110 transition-transform duration-300 [&_svg]:w-6 [&_svg]:h-6"> <div className="p-2 bg-neutral-800 rounded-lg shrink-0 group-hover:scale-110 transition-transform duration-300 [&_svg]:w-6 [&_svg]:h-6">
@@ -283,7 +283,7 @@ export default function IntegrationsPage() {
transition={{ duration: 0.5, delay: i * 0.05 }} transition={{ duration: 0.5, delay: i * 0.05 }}
> >
<Link <Link
href={`/integrations/${integration.id}`} href={integration.dedicatedPage ? `/integrations/${integration.id}` : '/integrations/script-tag'}
className="group relative p-6 bg-neutral-900/50 backdrop-blur-sm border border-neutral-800 rounded-2xl hover:border-brand-orange/50 transition-all duration-300 hover:-translate-y-1 hover:shadow-xl block h-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-orange focus-visible:ring-offset-2" className="group relative p-6 bg-neutral-900/50 backdrop-blur-sm border border-neutral-800 rounded-2xl hover:border-brand-orange/50 transition-all duration-300 hover:-translate-y-1 hover:shadow-xl block h-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-orange focus-visible:ring-offset-2"
> >
<div className="flex items-start justify-between mb-6"> <div className="flex items-start justify-between mb-6">

View File

@@ -1,118 +0,0 @@
'use client'
import Link from 'next/link'
import { ArrowLeftIcon } from '@ciphera-net/ui'
export default function ReactIntegrationPage() {
return (
<div className="relative min-h-screen flex flex-col overflow-hidden">
{/* * --- ATMOSPHERE (Background) --- */}
<div className="absolute inset-0 -z-10 pointer-events-none">
<div className="absolute bottom-0 right-1/4 w-[500px] h-[500px] bg-neutral-400/10 rounded-full blur-[128px] opacity-40" />
<div
className="absolute inset-0 bg-grid-pattern opacity-[0.05]"
style={{ maskImage: 'radial-gradient(ellipse at center, black 0%, transparent 70%)' }}
/>
</div>
<div className="flex-grow w-full max-w-4xl mx-auto px-4 pt-20 pb-10 z-10">
<Link
href="/integrations"
className="inline-flex items-center text-sm text-neutral-500 hover:text-brand-orange mb-8 transition-colors"
>
<ArrowLeftIcon className="w-4 h-4 mr-2" />
Back to Integrations
</Link>
<div className="flex items-center gap-4 mb-8">
<div className="p-3 bg-neutral-800 rounded-xl">
<svg viewBox="0 0 128 128" className="w-10 h-10 text-[#61DAFB] fill-current">
<path d="M64 10.6c18.4 0 34.6 5.8 44.6 14.8 6.4 5.8 10.2 12.8 10.2 20.6 0 21.6-28.6 41.2-64 41.2-1.6 0-3.2-.1-4.8-.2-1.2 10.8-6.2 20.2-13.8 27.6-8.8 8.6-20.6 13.4-33.2 13.4-2.2 0-4.4-.2-6.4-.4 10.2-12.8 15.6-29.2 15.6-46.2 0-2.6-.2-5.2-.4-7.8 13.6-1.6 26.2-5.4 37.4-11 11.2-5.6 20.2-13 26.2-21.4-6.4-5.8-15.4-10-25.6-12.2-10.2-2.2-21.4-3.4-33-3.4-1.6 0-3.2.1-4.8.2 1.2-10.8 6.2-20.2 13.8-27.6 8.8-8.6 20.6-13.4 33.2-13.4 2.2 0 4.4.2 6.4.4-10.2 12.8-15.6 29.2-15.6 46.2 0 2.6.2 5.2.4 7.8-13.6 1.6-26.2 5.4-37.4 11-11.2 5.6-20.2 13-26.2 21.4 6.4 5.8 15.4 10 25.6 12.2 10.2 2.2 21.4 3.4 33 3.4 1.6 0 3.2-.1 4.8-.2-1.2 10.8-6.2 20.2-13.8 27.6-8.8 8.6-20.6 13.4-33.2 13.4-2.2 0-4.4-.2-6.4-.4 10.2-12.8 15.6-29.2 15.6-46.2 0-2.6-.2-5.2-.4-7.8 13.6-1.6 26.2-5.4 37.4-11zm-33.4 62c-11.2 5.6-20.2 13-26.2 21.4 6.4 5.8 15.4 10 25.6 12.2 10.2 2.2 21.4 3.4 33 3.4 1.6 0 3.2-.1 4.8-.2-1.2 10.8-6.2 20.2-13.8 27.6-8.8 8.6-20.6 13.4-33.2 13.4-2.2 0-4.4-.2-6.4-.4 10.2-12.8 15.6-29.2 15.6-46.2 0-2.6-.2-5.2-.4-7.8 13.6-1.6 26.2-5.4 37.4-11zm-15.2-16.6c-6.4-5.8-10.2-12.8-10.2-20.6 0-21.6 28.6-41.2 64-41.2 1.6 0 3.2.1 4.8.2 1.2-10.8 6.2-20.2 13.8-27.6 8.8-8.6 20.6-13.4 33.2-13.4 2.2 0 4.4.2 6.4.4-10.2 12.8-15.6 29.2-15.6 46.2 0 2.6.2 5.2.4 7.8-13.6 1.6-26.2 5.4-37.4 11-11.2 5.6-20.2 13-26.2 21.4 6.4 5.8 15.4 10 25.6 12.2 10.2 2.2 21.4 3.4 33 3.4 1.6 0 3.2-.1 4.8-.2-1.2 10.8-6.2 20.2-13.8 27.6-8.8 8.6-20.6 13.4-33.2 13.4-2.2 0-4.4-.2-6.4-.4 10.2-12.8 15.6-29.2 15.6-46.2 0-2.6-.2-5.2-.4-7.8z" />
<circle cx="64" cy="64" r="10.6" />
</svg>
</div>
<h1 className="text-4xl md:text-5xl font-bold tracking-tight text-white">
React Integration
</h1>
</div>
<div className="prose prose-invert max-w-none">
<p className="lead text-xl text-neutral-400">
For standard React SPAs (Create React App, Vite, etc.), you can simply add the script tag to your <code>index.html</code>.
</p>
<hr className="my-8 border-neutral-800" />
<h3>Method 1: index.html (Recommended)</h3>
<p>
The simplest way is to add the script tag directly to the <code>&lt;head&gt;</code> of your <code>index.html</code> file.
</p>
<div className="bg-neutral-900 rounded-xl overflow-hidden border border-neutral-800 my-6">
<div className="flex items-center px-4 py-2 bg-neutral-800 border-b border-neutral-800">
<span className="text-xs text-neutral-400 font-mono">public/index.html</span>
</div>
<div className="p-4 overflow-x-auto">
<pre className="text-sm font-mono text-neutral-300">
{`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Pulse Analytics -->
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
<title>My React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>`}
</pre>
</div>
</div>
<h3>Method 2: Programmatic Injection</h3>
<p>
If you need to load the script dynamically (e.g., only in production), you can use a <code>useEffect</code> hook in your main App component.
</p>
<div className="bg-neutral-900 rounded-xl overflow-hidden border border-neutral-800 my-6">
<div className="flex items-center px-4 py-2 bg-neutral-800 border-b border-neutral-800">
<span className="text-xs text-neutral-400 font-mono">src/App.tsx</span>
</div>
<div className="p-4 overflow-x-auto">
<pre className="text-sm font-mono text-neutral-300">
{`import { useEffect } from 'react'
function App() {
useEffect(() => {
// Only load in production
if (process.env.NODE_ENV === 'production') {
const script = document.createElement('script')
script.defer = true
script.setAttribute('data-domain', 'your-site.com')
script.src = 'https://pulse.ciphera.net/script.js'
document.head.appendChild(script)
}
}, [])
return (
<div className="App">
<h1>Hello World</h1>
</div>
)
}`}
</pre>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,147 @@
import type { Metadata } from 'next'
import Link from 'next/link'
import { ArrowLeftIcon } from '@ciphera-net/ui'
import { CodeBlock } from '@ciphera-net/ui'
export const metadata: Metadata = {
title: 'Add Pulse Analytics to Any Website | Pulse by Ciphera',
description: 'Add privacy-first analytics to any website with a single script tag. Works with any platform, CMS, or framework.',
alternates: { canonical: 'https://pulse.ciphera.net/integrations/script-tag' },
openGraph: {
title: 'Add Pulse Analytics to Any Website | Pulse by Ciphera',
description: 'Add privacy-first analytics to any website with a single script tag.',
url: 'https://pulse.ciphera.net/integrations/script-tag',
siteName: 'Pulse by Ciphera',
type: 'article',
},
}
export default function ScriptTagPage() {
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'HowTo',
name: 'How to Add Pulse Analytics to Any Website',
description: 'Add privacy-first analytics to any website with a single script tag.',
step: [
{
'@type': 'HowToStep',
name: 'Copy the script tag',
text: 'Copy the Pulse tracking script with your domain.',
},
{
'@type': 'HowToStep',
name: 'Paste into your HTML head',
text: 'Add the script tag inside the <head> section of your website.',
},
{
'@type': 'HowToStep',
name: 'Deploy and verify',
text: 'Deploy your site and check the Pulse dashboard for incoming data.',
},
],
tool: {
'@type': 'HowToTool',
name: 'Pulse by Ciphera',
url: 'https://pulse.ciphera.net',
},
}
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<div className="relative min-h-screen flex flex-col overflow-hidden">
<div className="absolute inset-0 -z-10 pointer-events-none">
<div className="absolute bottom-0 right-1/4 w-[500px] h-[500px] bg-neutral-400/10 rounded-full blur-[128px] opacity-40" />
<div
className="absolute inset-0 bg-grid-pattern opacity-[0.05]"
style={{ maskImage: 'radial-gradient(ellipse at center, black 0%, transparent 70%)' }}
/>
</div>
<div className="flex-grow w-full max-w-4xl mx-auto px-4 pt-20 pb-10 z-10">
<Link
href="/integrations"
className="inline-flex items-center text-sm text-neutral-500 hover:text-brand-orange mb-8 transition-colors"
>
<ArrowLeftIcon className="w-4 h-4 mr-2" />
Back to Integrations
</Link>
<div className="flex items-center gap-4 mb-8">
<div className="p-3 bg-neutral-800 rounded-xl">
<svg className="w-10 h-10 text-brand-orange" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M17.25 6.75 22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3-4.5 16.5" />
</svg>
</div>
<h1 className="text-4xl md:text-5xl font-bold text-white">
Script Tag Integration
</h1>
</div>
<div className="prose prose-invert max-w-none">
<p className="lead text-xl text-neutral-400">
Add Pulse to any website by pasting a single script tag into your HTML.
This works with any platform, CMS, or static site.
</p>
<hr className="my-8 border-neutral-800" />
<h2>Installation</h2>
<p>
Add the following script tag inside the <code>&lt;head&gt;</code> section of your website:
</p>
<CodeBlock filename="index.html">{`<head>
<!-- ... other head elements ... -->
<script
defer
src="https://pulse.ciphera.net/script.js"
data-domain="your-site.com"
></script>
</head>`}</CodeBlock>
<h2>Configuration</h2>
<ul>
<li><code>data-domain</code> &mdash; your site&apos;s domain as shown in your Pulse dashboard (e.g. <code>example.com</code>), without <code>https://</code></li>
<li><code>defer</code> &mdash; loads the script without blocking page rendering</li>
</ul>
<h2>Where to paste the script</h2>
<p>
Most platforms have a &ldquo;Custom Code&rdquo;, &ldquo;Code Injection&rdquo;, or &ldquo;Header Scripts&rdquo;
section in their settings. Look for one of these:
</p>
<ul>
<li><strong>Squarespace:</strong> Settings &rarr; Developer Tools &rarr; Code Injection &rarr; Header</li>
<li><strong>Wix:</strong> Settings &rarr; Custom Code &rarr; Head</li>
<li><strong>Webflow:</strong> Project Settings &rarr; Custom Code &rarr; Head Code</li>
<li><strong>Ghost:</strong> Settings &rarr; Code Injection &rarr; Site Header</li>
<li><strong>Any HTML site:</strong> Paste directly into your <code>&lt;head&gt;</code> tag</li>
</ul>
<h2>Verify installation</h2>
<p>
After deploying, visit your site and check the Pulse dashboard. You should
see your first page view within a few seconds.
</p>
<hr className="my-8 border-neutral-800" />
<h3>Optional: Frustration Tracking</h3>
<p>
Detect rage clicks and dead clicks by adding the frustration tracking
add-on after the core script:
</p>
<CodeBlock filename="index.html">{`<script defer src="https://pulse.ciphera.net/script.frustration.js"></script>`}</CodeBlock>
<p>
No extra configuration needed. Add <code>data-no-rage</code> or{' '}
<code>data-no-dead</code> to disable individual signals.
</p>
</div>
</div>
</div>
</>
)
}

View File

@@ -1,112 +0,0 @@
'use client'
import Link from 'next/link'
import { ArrowLeftIcon } from '@ciphera-net/ui'
export default function VueIntegrationPage() {
return (
<div className="relative min-h-screen flex flex-col overflow-hidden">
{/* * --- ATMOSPHERE (Background) --- */}
<div className="absolute inset-0 -z-10 pointer-events-none">
<div className="absolute bottom-0 right-1/4 w-[500px] h-[500px] bg-neutral-400/10 rounded-full blur-[128px] opacity-40" />
<div
className="absolute inset-0 bg-grid-pattern opacity-[0.05]"
style={{ maskImage: 'radial-gradient(ellipse at center, black 0%, transparent 70%)' }}
/>
</div>
<div className="flex-grow w-full max-w-4xl mx-auto px-4 pt-20 pb-10 z-10">
<Link
href="/integrations"
className="inline-flex items-center text-sm text-neutral-500 hover:text-brand-orange mb-8 transition-colors"
>
<ArrowLeftIcon className="w-4 h-4 mr-2" />
Back to Integrations
</Link>
<div className="flex items-center gap-4 mb-8">
<div className="p-3 bg-neutral-800 rounded-xl">
<svg viewBox="0 0 128 128" className="w-10 h-10 text-[#4FC08D] fill-current">
<path d="M82.8 24.6h27.8L64 103.4 17.4 24.6h27.8L64 59.4l18.8-34.8z" />
<path d="M64 24.6H39L64 67.4l25-42.8H64z" fill="#35495E" />
</svg>
</div>
<h1 className="text-4xl md:text-5xl font-bold tracking-tight text-white">
Vue.js Integration
</h1>
</div>
<div className="prose prose-invert max-w-none">
<p className="lead text-xl text-neutral-400">
Integrating Pulse with Vue.js is straightforward. You can add the script to your <code>index.html</code> file.
</p>
<hr className="my-8 border-neutral-800" />
<h3>Method 1: index.html (Recommended)</h3>
<p>
Add the script tag to the <code>&lt;head&gt;</code> section of your <code>index.html</code> file. This works for both Vue 2 and Vue 3 projects created with Vue CLI or Vite.
</p>
<div className="bg-neutral-900 rounded-xl overflow-hidden border border-neutral-800 my-6">
<div className="flex items-center px-4 py-2 bg-neutral-800 border-b border-neutral-800">
<span className="text-xs text-neutral-400 font-mono">index.html</span>
</div>
<div className="p-4 overflow-x-auto">
<pre className="text-sm font-mono text-neutral-300">
{`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Pulse Analytics -->
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
<title>My Vue App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>`}
</pre>
</div>
</div>
<h3>Method 2: Nuxt.js</h3>
<p>
For Nuxt.js applications, you should add the script to your <code>nuxt.config.js</code> or <code>nuxt.config.ts</code> file.
</p>
<div className="bg-neutral-900 rounded-xl overflow-hidden border border-neutral-800 my-6">
<div className="flex items-center px-4 py-2 bg-neutral-800 border-b border-neutral-800">
<span className="text-xs text-neutral-400 font-mono">nuxt.config.ts</span>
</div>
<div className="p-4 overflow-x-auto">
<pre className="text-sm font-mono text-neutral-300">
{`export default defineNuxtConfig({
app: {
head: {
script: [
{
src: 'https://pulse.ciphera.net/script.js',
defer: true,
'data-domain': 'your-site.com'
}
]
}
}
})`}
</pre>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@@ -1,80 +0,0 @@
'use client'
import Link from 'next/link'
import { ArrowLeftIcon } from '@ciphera-net/ui'
export default function WordPressIntegrationPage() {
return (
<div className="relative min-h-screen flex flex-col overflow-hidden">
{/* * --- ATMOSPHERE (Background) --- */}
<div className="absolute inset-0 -z-10 pointer-events-none">
<div className="absolute bottom-0 right-1/4 w-[500px] h-[500px] bg-neutral-400/10 rounded-full blur-[128px] opacity-40" />
<div
className="absolute inset-0 bg-grid-pattern opacity-[0.05]"
style={{ maskImage: 'radial-gradient(ellipse at center, black 0%, transparent 70%)' }}
/>
</div>
<div className="flex-grow w-full max-w-4xl mx-auto px-4 pt-20 pb-10 z-10">
<Link
href="/integrations"
className="inline-flex items-center text-sm text-neutral-500 hover:text-brand-orange mb-8 transition-colors"
>
<ArrowLeftIcon className="w-4 h-4 mr-2" />
Back to Integrations
</Link>
<div className="flex items-center gap-4 mb-8">
<div className="p-3 bg-neutral-800 rounded-xl">
<svg viewBox="0 0 128 128" className="w-10 h-10 text-[#21759B] fill-current">
<path d="M116.6 64c0-19.2-10.4-36-26-45.2l28.6 78.4c-1 3.2-2.2 6.2-3.6 9.2-11.4 12.4-27.8 20.2-46 20.2-6.2 0-12.2-.8-17.8-2.4l26.2-76.4c1.2.2 2.4.4 3.6.4 5.4 0 13.8-.8 13.8-.8 2.8-.2 3.2 4 .4 4.2 0 0-2.8.2-6 .4l19 56.6 5.4-18c2.4-7.4 4.2-12.8 4.2-17.4 0-6-2.2-10.2-7.6-12.6-2.8-1.2-2.2-5.4 1.4-5.4h4.4zM64 121.2c-15.8 0-30.2-6.4-40.8-16.8L46.6 36.8c-2.8-.2-5.8-.4-5.8-.4-2.8-.2-2.4-4.4.4-4.2 0 0 8.4.8 13.6.8 5.4 0 13.6-.8 13.6-.8 2.8-.2 3.2 4 .4 4.2 0 0-2.8.2-5.8.4l18.2 54.4 10.6-31.8L64 121.2zM11.4 64c0 17 8.2 32.2 20.8 41.8L18.8 66.8c-.8-3.4-1.2-6.6-1.2-9.2 0-6.8 2.6-13 6.2-17.8C15.6 47.4 11.4 55.2 11.4 64zM64 6.8c16.2 0 30.8 6.8 41.4 17.6-1.4-.2-2.8-.2-4.2-.2-7.8 0-14.2 1.4-14.2 1.4-2.8.6-2.2 4.8.6 4.2 0 0 5-1 10.6-1 2.2 0 4.6.2 6.6.4L88.2 53 71.4 6.8h-7.4z" />
</svg>
</div>
<h1 className="text-4xl md:text-5xl font-bold tracking-tight text-white">
WordPress Integration
</h1>
</div>
<div className="prose prose-invert max-w-none">
<p className="lead text-xl text-neutral-400">
You can add Pulse to your WordPress site without installing any heavy plugins, or by using a simple code snippet plugin.
</p>
<hr className="my-8 border-neutral-800" />
<h3>Method 1: Using a Plugin (Easiest)</h3>
<ol>
<li>Install a plugin like "Insert Headers and Footers" (WPCode).</li>
<li>Go to the plugin settings and find the "Scripts in Header" section.</li>
<li>Paste the following code snippet:</li>
</ol>
<div className="bg-neutral-900 rounded-xl overflow-hidden border border-neutral-800 my-6">
<div className="flex items-center px-4 py-2 bg-neutral-800 border-b border-neutral-800">
<span className="text-xs text-neutral-400 font-mono">Header Script</span>
</div>
<div className="p-4 overflow-x-auto">
<pre className="text-sm font-mono text-neutral-300">
{`<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>`}
</pre>
</div>
</div>
<h3>Method 2: Edit Theme Files (Advanced)</h3>
<p>
If you are comfortable editing your theme files, you can add the script directly to your <code>header.php</code> file.
</p>
<ol>
<li>Go to Appearance &gt; Theme File Editor.</li>
<li>Select <code>header.php</code> from the right sidebar.</li>
<li>Paste the script tag just before the closing <code>&lt;/head&gt;</code> tag.</li>
</ol>
</div>
</div>
</div>
)
}

View File

@@ -1,9 +1,13 @@
import type { MetadataRoute } from 'next' import type { MetadataRoute } from 'next'
import { integrations } from '@/lib/integrations' import { integrations } from '@/lib/integrations'
import { getIntegrationGuides } from '@/lib/integration-content'
const BASE_URL = 'https://pulse.ciphera.net' const BASE_URL = 'https://pulse.ciphera.net'
export default function sitemap(): MetadataRoute.Sitemap { export default function sitemap(): MetadataRoute.Sitemap {
const guides = getIntegrationGuides()
const guidesBySlug = new Map(guides.map((g) => [g.slug, g]))
const publicRoutes = [ const publicRoutes = [
{ url: '', priority: 1.0, changeFrequency: 'weekly' as const }, { url: '', priority: 1.0, changeFrequency: 'weekly' as const },
{ url: '/about', priority: 0.8, changeFrequency: 'monthly' as const }, { url: '/about', priority: 0.8, changeFrequency: 'monthly' as const },
@@ -13,20 +17,33 @@ export default function sitemap(): MetadataRoute.Sitemap {
{ url: '/changelog', priority: 0.6, changeFrequency: 'weekly' as const }, { url: '/changelog', priority: 0.6, changeFrequency: 'weekly' as const },
{ url: '/installation', priority: 0.8, changeFrequency: 'monthly' as const }, { url: '/installation', priority: 0.8, changeFrequency: 'monthly' as const },
{ url: '/integrations', priority: 0.8, changeFrequency: 'monthly' as const }, { url: '/integrations', priority: 0.8, changeFrequency: 'monthly' as const },
{ url: '/integrations/script-tag', priority: 0.6, changeFrequency: 'monthly' as const },
] ]
const integrationRoutes = integrations.map((i) => ({ const integrationRoutes = integrations
.filter((i) => i.dedicatedPage)
.map((i) => {
const guide = guidesBySlug.get(i.id)
return {
url: `/integrations/${i.id}`, url: `/integrations/${i.id}`,
priority: 0.7, priority: 0.7,
changeFrequency: 'monthly' as const, changeFrequency: 'monthly' as const,
})) lastModified: guide?.date ? new Date(guide.date) : new Date('2026-03-28'),
}
})
const allRoutes = [...publicRoutes, ...integrationRoutes] return [
...publicRoutes.map((route) => ({
return allRoutes.map((route) => ({
url: `${BASE_URL}${route.url}`, url: `${BASE_URL}${route.url}`,
lastModified: new Date(), lastModified: new Date('2026-03-28'),
changeFrequency: route.changeFrequency, changeFrequency: route.changeFrequency,
priority: route.priority, priority: route.priority,
})) })),
...integrationRoutes.map((route) => ({
url: `${BASE_URL}${route.url}`,
lastModified: route.lastModified,
changeFrequency: route.changeFrequency,
priority: route.priority,
})),
]
} }

View File

@@ -0,0 +1,39 @@
---
title: "Angular"
description: "Add Pulse analytics to your Angular application. Simple index.html setup for all Angular versions."
category: "framework"
brandColor: "#0F0F11"
officialUrl: "https://angular.dev"
relatedIds: ["react", "vue"]
date: "2026-03-28"
---
Add the script to your `src/index.html` — the single entry point for all Angular applications.
---
## Add the Pulse script to index.html
Place the Pulse script inside the `<head>` tag of your Angular app's `src/index.html`.
<CodeBlock filename="src/index.html">{`<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
<title>My Angular App</title>
<base href="/">
</head>
<body>
<app-root></app-root>
</body>
</html>`}</CodeBlock>
For more details, see the [Angular docs](https://angular.dev/guide/components).

View File

@@ -0,0 +1,48 @@
---
title: "Astro"
description: "Integrate Pulse analytics with Astro. Add the script to your base layout for all pages."
category: "framework"
brandColor: "#BC52EE"
officialUrl: "https://docs.astro.build"
relatedIds: ["svelte", "hugo", "eleventy"]
date: "2026-03-28"
---
Add the Pulse script to your base layout so it's included on every page of your Astro site.
---
## Add to your base layout
Place the Pulse script in the `<head>` of your shared layout component.
<CodeBlock filename="src/layouts/BaseLayout.astro">{`---
interface Props {
title: string
}
const { title } = Astro.props
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
<title>{title}</title>
</head>
<body>
<slot />
</body>
</html>`}</CodeBlock>
If you're using Astro's View Transitions, the script will persist across navigations by default since it's in the `<head>`.
For more details, see the [Astro scripts docs](https://docs.astro.build/en/guides/client-side-scripts/).

View File

@@ -0,0 +1,42 @@
---
title: "Django"
description: "Add Pulse analytics to your Django app. Template-based integration for all Django versions."
category: "backend"
brandColor: "#092E20"
officialUrl: "https://docs.djangoproject.com"
relatedIds: ["flask", "laravel", "htmx"]
date: "2026-03-28"
---
Add the Pulse script to your base template with a debug guard.
---
## Add to your base template
Use Django's template tags to only load the script when `DEBUG` is `False`.
<CodeBlock filename="templates/base.html">{`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% if not debug %}
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
{% endif %}
<title>{% block title %}My Django App{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>`}</CodeBlock>
Make sure to pass `debug` to the template context via `settings.DEBUG`, or use a context processor to make it available globally.
For more details, see the [Django template docs](https://docs.djangoproject.com/en/stable/ref/templates/builtins/).

View File

@@ -0,0 +1,45 @@
---
title: "Drupal"
description: "Add Pulse analytics to your Drupal site using a module or theme template."
category: "cms"
brandColor: "#0678BE"
officialUrl: "https://www.drupal.org/docs"
relatedIds: ["wordpress", "joomla"]
date: "2026-03-28"
---
Add the Pulse script via a contributed module or by editing your theme's Twig template.
---
## Method 1: Using Asset Injector module
Install the [Asset Injector](https://www.drupal.org/project/asset_injector) module and create a new JS injector with the Pulse script. Set it to load on all pages in the header region.
## Method 2: Edit html.html.twig
Add the script directly to your theme's `html.html.twig` template in the head area.
<CodeBlock filename="templates/html.html.twig">{`<!DOCTYPE html>
<html{{ html_attributes }}>
<head>
<head-placeholder token="{{ placeholder_token }}">
<title>{{ head_title|safe_join(' | ') }}</title>
<css-placeholder token="{{ placeholder_token }}">
<js-placeholder token="{{ placeholder_token }}">
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
</head>
<body{{ attributes }}>
{{ page_top }}
{{ page }}
{{ page_bottom }}
<js-bottom-placeholder token="{{ placeholder_token }}">
</body>
</html>`}</CodeBlock>
For more details, see the [Drupal theming docs](https://www.drupal.org/docs/theming-drupal).

View File

@@ -0,0 +1,38 @@
---
title: "Eleventy"
description: "Add Pulse analytics to your Eleventy (11ty) site. Template-based integration."
category: "ssg"
brandColor: "#222222"
officialUrl: "https://www.11ty.dev/docs"
relatedIds: ["hugo", "jekyll", "astro"]
date: "2026-03-28"
---
Add the Pulse script to your base Nunjucks or Liquid layout.
---
## Add to your base layout
Place the Pulse script inside the `<head>` of your shared base template.
<CodeBlock filename="src/_includes/base.njk">{`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
<title>{{ title }}</title>
</head>
<body>
{{ content | safe }}
</body>
</html>`}</CodeBlock>
For more details, see the [Eleventy layouts docs](https://www.11ty.dev/docs/layouts/).

View File

@@ -0,0 +1,61 @@
---
title: "Express"
description: "Serve Pulse analytics from your Express.js app. Middleware or template-based setup."
category: "backend"
brandColor: "#000000"
officialUrl: "https://expressjs.com"
relatedIds: ["flask", "nextjs", "react"]
date: "2026-03-28"
---
Add the Pulse script to your template engine's layout (EJS, Pug, Handlebars) or serve it via static HTML.
---
## Method 1: EJS template
If you use EJS as your template engine, add the script to your layout with a production guard.
<CodeBlock filename="views/layout.ejs">{`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<% if (process.env.NODE_ENV === 'production') { %>
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
<% } %>
<title><%= title %></title>
</head>
<body>
<%- body %>
</body>
</html>`}</CodeBlock>
## Method 2: Static HTML
If you serve static HTML files via Express, add the script directly.
<CodeBlock filename="public/index.html">{`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
<title>My Express App</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>`}</CodeBlock>

View File

@@ -0,0 +1,40 @@
---
title: "Flask"
description: "Add Pulse analytics to your Flask app. Jinja2 template integration."
category: "backend"
brandColor: "#3BABC3"
officialUrl: "https://flask.palletsprojects.com"
relatedIds: ["django", "htmx", "express"]
date: "2026-03-28"
---
Add the Pulse script to your Jinja2 base template with a debug guard.
---
## Add to your base template
Use Jinja2's conditional to only load the script when `DEBUG` is off.
<CodeBlock filename="templates/base.html">{`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% if not config.DEBUG %}
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
{% endif %}
<title>{% block title %}My Flask App{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>`}</CodeBlock>
For more details, see the [Flask template docs](https://flask.palletsprojects.com/en/stable/patterns/templateinheritance/).

View File

@@ -0,0 +1,25 @@
---
title: "Framer"
description: "Add Pulse analytics to your Framer site via custom code settings."
category: "platform"
brandColor: "#0055FF"
officialUrl: "https://www.framer.com/help"
relatedIds: ["webflow", "squarespace", "wix"]
date: "2026-03-28"
---
Add the Pulse script to your Framer project's custom code settings.
---
## Add via Project Settings
Go to **Project Settings -> General -> Custom Code -> Head** and paste the Pulse script.
<CodeBlock filename="Project Settings -> Head">{`<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>`}</CodeBlock>
For more details, see the [Framer custom code docs](https://www.framer.com/help/articles/custom-code/).

View File

@@ -0,0 +1,52 @@
---
title: "Gatsby"
description: "Add Pulse analytics to your Gatsby site using gatsby-ssr or the Gatsby Head API."
category: "ssg"
brandColor: "#663399"
officialUrl: "https://www.gatsbyjs.com/docs"
relatedIds: ["react", "nextjs", "hugo"]
date: "2026-03-28"
---
Use the Gatsby SSR API or the Gatsby Head API to add Pulse to your site.
---
## Method 1: gatsby-ssr.js
Use the `onRenderBody` hook to inject the Pulse script into every page's `<head>`.
<CodeBlock filename="gatsby-ssr.js">{`import React from "react"
export const onRenderBody = ({ setHeadComponents }) => {
setHeadComponents([
<script
key="pulse-analytics"
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
/>,
])
}`}</CodeBlock>
## Method 2: Gatsby Head API (v4.19+)
If you're on Gatsby 4.19 or later, you can use the Head export in any page or template component.
<CodeBlock filename="src/pages/index.tsx">{`import React from "react"
export function Head() {
return (
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
/>
)
}
export default function IndexPage() {
return <h1>Hello World</h1>
}`}</CodeBlock>
For more details, see the [Gatsby Head API docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-head/).

View File

@@ -0,0 +1,27 @@
---
title: "Ghost"
description: "Add Pulse analytics to your Ghost blog via Code Injection settings."
category: "cms"
brandColor: "#15171A"
officialUrl: "https://ghost.org/docs"
relatedIds: ["wordpress", "blogger"]
date: "2026-03-28"
---
Use Ghost's built-in Code Injection feature to add the Pulse script — no theme editing required.
---
## Add via Code Injection
Go to **Settings -> Code injection -> Site Header** and paste the Pulse script.
<CodeBlock filename="Settings -> Code injection -> Site Header">{`<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>`}</CodeBlock>
Alternatively, you can add the script directly to your theme's `default.hbs` template file.
For more details, see the [Ghost themes docs](https://ghost.org/docs/themes/).

View File

@@ -0,0 +1,39 @@
---
title: "Google Tag Manager"
description: "Add Pulse analytics via Google Tag Manager. Works with any site using GTM."
category: "platform"
brandColor: "#246FDB"
officialUrl: "https://tagmanager.google.com"
relatedIds: ["wordpress", "shopify", "webflow"]
date: "2026-03-28"
---
Add Pulse via Google Tag Manager — works with any site that already has GTM installed.
---
## Create a Custom HTML tag
Follow these steps to add Pulse through GTM:
1. Go to **Tags -> New -> Custom HTML**
2. Paste the snippet below
3. Set the trigger to **All Pages**
4. Publish your container
<CodeBlock filename="GTM -> Custom HTML Tag">{`<script defer src="https://pulse.ciphera.net/script.js"></script>`}</CodeBlock>
That's it. Pulse auto-detects the domain from the page, so no extra configuration is needed.
<details>
<summary className="cursor-pointer text-sm text-neutral-400 hover:text-neutral-700 dark:hover:text-neutral-300">Advanced: override domain or configure options</summary>
If your site is registered under a different domain than the page hostname, or you need custom options (API endpoint, storage mode, etc.), use `pulseConfig`:
<CodeBlock filename="GTM -> Custom HTML Tag (with config)">{`<script>
window.pulseConfig = { domain: "your-site.com" };
</script>
<script defer src="https://pulse.ciphera.net/script.js"></script>`}</CodeBlock>
</details>
For more details, see the [GTM Custom HTML tag docs](https://support.google.com/tagmanager/answer/6103696).

View File

@@ -0,0 +1,44 @@
---
title: "Hugo"
description: "Add Pulse analytics to your Hugo site via a partial or base template."
category: "ssg"
brandColor: "#FF4088"
officialUrl: "https://gohugo.io/documentation"
relatedIds: ["jekyll", "eleventy", "astro"]
date: "2026-03-28"
---
Add the Pulse script via a Hugo partial or directly in your base template.
---
## Method 1: Create a partial
Create an analytics partial with a production guard using Hugo's `.Site.IsServer` flag.
<CodeBlock filename="layouts/partials/analytics.html">{`{{ if not .Site.IsServer }}
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
{{ end }}`}</CodeBlock>
## Method 2: Include the partial in your base layout
Add the partial to your `baseof.html` layout.
<CodeBlock filename="layouts/_default/baseof.html">{`<!DOCTYPE html>
<html lang="{{ .Site.Language }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{ partial "analytics.html" . }}
<title>{{ .Title }}</title>
</head>
<body>
{{ block "main" . }}{{ end }}
</body>
</html>`}</CodeBlock>
For more details, see the [Hugo partials docs](https://gohugo.io/templates/partials/).

View File

@@ -0,0 +1,41 @@
---
title: "Laravel"
description: "Add Pulse analytics to your Laravel app. Blade template integration for all Laravel versions."
category: "backend"
brandColor: "#FF2D20"
officialUrl: "https://laravel.com/docs"
relatedIds: ["django", "rails", "wordpress"]
date: "2026-03-28"
---
Add the Pulse script to your Blade layout template with a production guard.
---
## Add to your Blade layout
Use Laravel's `@production` directive to only load the script in production.
<CodeBlock filename="resources/views/layouts/app.blade.php">{`<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@production
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
@endproduction
<title>@yield('title')</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
@yield('content')
</body>
</html>`}</CodeBlock>
For more details, see the [Laravel @production docs](https://laravel.com/docs/blade#the-production-directive).

View File

@@ -0,0 +1,68 @@
---
title: "Next.js"
description: "Step-by-step guide to adding Pulse privacy-first analytics to your Next.js app with next/script. Covers App Router and Pages Router."
category: "framework"
brandColor: "#000000"
officialUrl: "https://nextjs.org/docs"
relatedIds: ["react", "vercel", "nuxt"]
date: "2026-03-28"
---
The best way to add Pulse to your Next.js application is using the built-in `next/script` component.
---
## Method 1: App Router
Add the Pulse script to your root layout so it loads on every page.
<CodeBlock filename="app/layout.tsx">{`import Script from 'next/script'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<Script
defer
src="https://pulse.ciphera.net/script.js"
data-domain="your-site.com"
strategy="afterInteractive"
/>
</head>
<body>{children}</body>
</html>
)
}`}</CodeBlock>
## Method 2: Pages Router
If you're using the Pages Router, add the script to your custom `_app.tsx`.
<CodeBlock filename="pages/_app.tsx">{`import Script from 'next/script'
import type { AppProps } from 'next/app'
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Script
defer
src="https://pulse.ciphera.net/script.js"
data-domain="your-site.com"
strategy="afterInteractive"
/>
<Component {...pageProps} />
</>
)
}`}</CodeBlock>
## Configuration options
- `data-domain` — your site's domain (without `https://`)
- `src` — the Pulse script URL
- `strategy="afterInteractive"` — loads the script after the page becomes interactive
For more details, see the [Next.js Script docs](https://nextjs.org/docs/app/api-reference/components/script).

View File

@@ -0,0 +1,49 @@
---
title: "Nuxt"
description: "Configure Pulse analytics in Nuxt 2 or Nuxt 3 via nuxt.config. Simple, framework-native setup."
category: "framework"
brandColor: "#00DC82"
officialUrl: "https://nuxt.com/docs"
relatedIds: ["vue", "nextjs", "vitepress"]
date: "2026-03-28"
---
Configure Pulse analytics in your `nuxt.config` for a framework-native setup.
---
## Method 1: Nuxt 3
Add the Pulse script via the `app.head` option in your Nuxt 3 config.
<CodeBlock filename="nuxt.config.ts">{`export default defineNuxtConfig({
app: {
head: {
script: [
{
defer: true,
'data-domain': 'your-site.com',
src: 'https://pulse.ciphera.net/script.js',
},
],
},
},
})`}</CodeBlock>
## Method 2: Nuxt 2
In Nuxt 2, use the `head` property in your config.
<CodeBlock filename="nuxt.config.js">{`export default {
head: {
script: [
{
defer: true,
'data-domain': 'your-site.com',
src: 'https://pulse.ciphera.net/script.js',
},
],
},
}`}</CodeBlock>
For more details, see the [Nuxt head config docs](https://nuxt.com/docs/api/nuxt-config#head).

View File

@@ -0,0 +1,42 @@
---
title: "Ruby on Rails"
description: "Add Pulse analytics to your Ruby on Rails app. ERB layout integration."
category: "backend"
brandColor: "#D30001"
officialUrl: "https://guides.rubyonrails.org"
relatedIds: ["laravel", "django", "jekyll"]
date: "2026-03-28"
---
Add the Pulse script to your application layout with a production environment guard.
---
## Add to your application layout
Use an `if` guard to only load the script in production.
<CodeBlock filename="app/views/layouts/application.html.erb">{`<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<% if Rails.env.production? %>
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
<% end %>
<title><%= yield(:title) || "My Rails App" %></title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag "application" %>
</head>
<body>
<%= yield %>
</body>
</html>`}</CodeBlock>
For more details, see the [Rails layout docs](https://guides.rubyonrails.org/layouts_and_rendering.html).

View File

@@ -0,0 +1,56 @@
---
title: "React"
description: "Integrate Pulse analytics with any React SPA — Create React App, Vite, or custom setups. Two easy methods."
category: "framework"
brandColor: "#61DAFB"
officialUrl: "https://react.dev"
relatedIds: ["nextjs", "remix", "gatsby", "preact"]
date: "2026-03-28"
---
For standard React SPAs, add the script to your `index.html`.
---
## Method 1: index.html (Recommended)
The simplest approach is to add the Pulse script directly to your HTML entry point.
<CodeBlock filename="public/index.html">{`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
<title>My React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>`}</CodeBlock>
## Method 2: Programmatic injection via useEffect
If you prefer to inject the script programmatically (e.g. only in production), use a `useEffect` hook.
<CodeBlock filename="src/App.tsx">{`import { useEffect } from 'react'
function App() {
useEffect(() => {
if (process.env.NODE_ENV === 'production') {
const script = document.createElement('script')
script.defer = true
script.setAttribute('data-domain', 'your-site.com')
script.src = 'https://pulse.ciphera.net/script.js'
document.head.appendChild(script)
}
}, [])
return <div className="App"><h1>Hello World</h1></div>
}`}</CodeBlock>

View File

@@ -0,0 +1,50 @@
---
title: "Remix"
description: "Add Pulse analytics to your Remix application via the root route. Simple script tag in app/root.tsx."
category: "framework"
brandColor: "#000000"
officialUrl: "https://remix.run/docs"
relatedIds: ["react", "nextjs"]
date: "2026-03-28"
---
Add the Pulse script to your `app/root.tsx` so it's included on every route.
---
## Add script to app/root.tsx
The root route is the top-level layout in Remix. Add the Pulse script inside the `<head>` section.
<CodeBlock filename="app/root.tsx">{`import {
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from '@remix-run/react'
export default function App() {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
/>
</head>
<body>
<Outlet />
<ScrollRestoration />
<Scripts />
</body>
</html>
)
}`}</CodeBlock>
For more details, see the [Remix root docs](https://remix.run/docs/en/main/file-conventions/root).

View File

@@ -0,0 +1,45 @@
---
title: "Script Tag"
description: "Add privacy-first analytics to any website with a single script tag. Works with any platform, CMS, or framework."
category: "platform"
brandColor: "#F97316"
officialUrl: "https://pulse.ciphera.net"
relatedIds: []
date: "2026-03-28"
---
Add Pulse to any website by pasting a single script tag into your HTML. This works with any platform, CMS, or static site.
---
## Installation
Add the following script tag inside the `<head>` section of your website:
<CodeBlock filename="index.html">{`<head>
<!-- ... other head elements ... -->
<script
defer
src="https://pulse.ciphera.net/script.js"
data-domain="your-site.com"
></script>
</head>`}</CodeBlock>
## Configuration
- `data-domain` — your site's domain as shown in your Pulse dashboard (e.g. `example.com`), without `https://`
- `defer` — loads the script without blocking page rendering
## Where to paste the script
Most platforms have a "Custom Code", "Code Injection", or "Header Scripts" section in their settings. Look for one of these:
- **Squarespace:** Settings -> Developer Tools -> Code Injection -> Header
- **Wix:** Settings -> Custom Code -> Head
- **Webflow:** Project Settings -> Custom Code -> Head Code
- **Ghost:** Settings -> Code Injection -> Site Header
- **Any HTML site:** Paste directly into your `<head>` tag
## Verify installation
After deploying, visit your site and check the Pulse dashboard. You should see your first page view within a few seconds.

View File

@@ -0,0 +1,32 @@
---
title: "Shopify"
description: "Add Pulse privacy-first analytics to your Shopify store via the theme editor."
category: "ecommerce"
brandColor: "#7AB55C"
officialUrl: "https://shopify.dev/docs"
relatedIds: ["woocommerce", "bigcommerce", "prestashop"]
date: "2026-03-28"
---
Add the Pulse script via the Shopify theme editor — no app needed.
---
## Method 1: Edit theme code
Go to **Online Store -> Themes -> Edit code** and open `layout/theme.liquid`. Add the Pulse script before the closing `</head>` tag.
<CodeBlock filename="layout/theme.liquid">{`<!-- Add before </head> -->
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>`}</CodeBlock>
## Method 2: Shopify Plus — Customer Events
If you're on Shopify Plus, you can add the Pulse script via **Settings -> Customer Events -> Custom Pixels**.
Use your custom domain or `.myshopify.com` domain as the `data-domain` value.
For more details, see the [Shopify theme docs](https://shopify.dev/docs/storefronts/themes/architecture/layouts).

View File

@@ -0,0 +1,27 @@
---
title: "Squarespace"
description: "Add Pulse analytics to your Squarespace site via the Code Injection panel."
category: "platform"
brandColor: "#000000"
officialUrl: "https://support.squarespace.com"
relatedIds: ["webflow", "wix", "carrd"]
date: "2026-03-28"
---
Use Squarespace's Code Injection feature to add the Pulse script.
---
## Add via Code Injection
Go to **Settings -> Developer Tools -> Code Injection -> Header** and paste the Pulse script.
<CodeBlock filename="Settings -> Code Injection -> Header">{`<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>`}</CodeBlock>
**Note:** Code Injection requires a Business or Commerce plan.
For more details, see the [Squarespace code injection docs](https://support.squarespace.com/hc/en-us/articles/205815928).

View File

@@ -0,0 +1,53 @@
---
title: "Svelte"
description: "Add Pulse analytics to Svelte or SvelteKit. Simple setup for both Vite-based Svelte and SvelteKit applications."
category: "framework"
brandColor: "#FF3E00"
officialUrl: "https://svelte.dev"
relatedIds: ["astro", "vue"]
date: "2026-03-28"
---
Add the script to your `index.html` for Vite-based Svelte, or use `<svelte:head>` in SvelteKit.
---
## Method 1: Svelte (Vite)
For standard Svelte projects using Vite, add the Pulse script to your `index.html`.
<CodeBlock filename="index.html">{`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
<title>My Svelte App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>`}</CodeBlock>
## Method 2: SvelteKit
In SvelteKit, use `<svelte:head>` in your root layout to add the script to every page.
<CodeBlock filename="src/routes/+layout.svelte">{`<svelte:head>
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
</svelte:head>
<slot />`}</CodeBlock>
Alternatively, you can add the script directly to `src/app.html` in your SvelteKit project.

View File

@@ -0,0 +1,39 @@
---
title: "Vue.js"
description: "Add Pulse privacy-first analytics to your Vue.js app. Works with Vue 2, Vue 3, Vue CLI, and Vite."
category: "framework"
brandColor: "#4FC08D"
officialUrl: "https://vuejs.org"
relatedIds: ["nuxt", "vitepress"]
date: "2026-03-28"
---
Add the script to your `index.html` — works for both Vue CLI and Vite-based projects.
---
## Add the Pulse script to index.html
Both Vue CLI and Vite use an `index.html` as the entry point. Simply add the Pulse script inside the `<head>` tag.
<CodeBlock filename="index.html">{`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>
<title>My Vue App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>`}</CodeBlock>
Looking for Nuxt? Check the dedicated [Nuxt guide](/integrations/nuxt).

View File

@@ -0,0 +1,27 @@
---
title: "Webflow"
description: "Add Pulse analytics to your Webflow site via project custom code settings."
category: "platform"
brandColor: "#146EF5"
officialUrl: "https://university.webflow.com"
relatedIds: ["squarespace", "wix", "framer"]
date: "2026-03-28"
---
Paste the Pulse script into your Webflow project's custom code settings.
---
## Add via Project Settings
Go to **Project Settings -> Custom Code -> Head Code** and paste the Pulse script.
<CodeBlock filename="Project Settings -> Head Code">{`<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>`}</CodeBlock>
**Note:** Custom code requires a paid Webflow site plan. The script won't appear in the Designer preview — publish your site to see it in action.
For more details, see the [Webflow custom code docs](https://university.webflow.com/lesson/custom-code-in-the-head-and-body-tag).

View File

@@ -0,0 +1,27 @@
---
title: "Wix"
description: "Add Pulse analytics to your Wix site via Custom Code settings."
category: "platform"
brandColor: "#0C6EFC"
officialUrl: "https://support.wix.com"
relatedIds: ["webflow", "squarespace", "framer"]
date: "2026-03-28"
---
Use Wix's Custom Code settings to add the Pulse script.
---
## Add via Custom Code
Go to **Settings -> Custom Code -> Add Custom Code**. Set the placement to **Head** and apply it to **All pages**.
<CodeBlock filename="Custom Code Snippet">{`<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>`}</CodeBlock>
**Note:** Custom Code requires a Wix Premium plan.
For more details, see the [Wix custom code docs](https://support.wix.com/en/article/embedding-custom-code-on-your-site).

View File

@@ -0,0 +1,27 @@
---
title: "WordPress"
description: "Add Pulse analytics to your WordPress site via a plugin or theme header code."
category: "cms"
brandColor: "#21759B"
officialUrl: "https://wordpress.org/documentation"
relatedIds: ["ghost", "drupal", "woocommerce"]
date: "2026-03-28"
---
Add the Pulse script via a plugin or by editing your theme's header file directly.
---
## Method 1: Using a plugin (Recommended)
The easiest way is to use the [WPCode (Insert Headers and Footers)](https://wordpress.org/plugins/insert-headers-and-footers/) plugin. Install it, then go to **Code Snippets -> Header & Footer** and paste the Pulse script into the Header section.
<CodeBlock filename="Header Script">{`<script
defer
data-domain="your-site.com"
src="https://pulse.ciphera.net/script.js"
></script>`}</CodeBlock>
## Method 2: Edit header.php directly
Go to **Appearance -> Theme File Editor** and edit `header.php`. Add the Pulse script before the closing `</head>` tag.

View File

@@ -0,0 +1,63 @@
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
const CONTENT_DIR = path.join(process.cwd(), 'content', 'integrations')
export interface IntegrationGuideMeta {
slug: string
title: string
description: string
category: string
brandColor: string
officialUrl: string
relatedIds: string[]
date: string
}
export interface IntegrationGuideArticle extends IntegrationGuideMeta {
content: string
}
export function getIntegrationGuides(): IntegrationGuideMeta[] {
if (!fs.existsSync(CONTENT_DIR)) return []
const files = fs.readdirSync(CONTENT_DIR).filter((f) => f.endsWith('.mdx'))
return files.map((filename) => {
const slug = filename.replace(/\.mdx$/, '')
const raw = fs.readFileSync(path.join(CONTENT_DIR, filename), 'utf-8')
const { data } = matter(raw)
return {
slug,
title: data.title,
description: data.description,
category: data.category,
brandColor: data.brandColor,
officialUrl: data.officialUrl,
relatedIds: data.relatedIds || [],
date: data.date,
}
})
}
export function getIntegrationGuide(slug: string): IntegrationGuideArticle | null {
const filePath = path.join(CONTENT_DIR, `${slug}.mdx`)
if (!fs.existsSync(filePath)) return null
const raw = fs.readFileSync(filePath, 'utf-8')
const { data, content } = matter(raw)
return {
slug,
title: data.title,
description: data.description,
category: data.category,
brandColor: data.brandColor,
officialUrl: data.officialUrl,
relatedIds: data.relatedIds || [],
date: data.date,
content,
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -34,10 +34,10 @@ export interface Integration {
icon: ReactNode icon: ReactNode
/** URL to official documentation / website */ /** URL to official documentation / website */
officialUrl: string officialUrl: string
/** SEO meta description for this integration's guide page */
seoDescription: string
/** Related integration IDs for cross-linking */ /** Related integration IDs for cross-linking */
relatedIds: string[] relatedIds: string[]
/** Whether this integration has a dedicated guide page */
dedicatedPage: boolean
} }
// * ─── Category labels (for UI grouping) ────────────────────────────────────── // * ─── Category labels (for UI grouping) ──────────────────────────────────────
@@ -79,9 +79,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://nextjs.org/docs', officialUrl: 'https://nextjs.org/docs',
seoDescription:
'Step-by-step guide to adding Pulse privacy-first analytics to your Next.js app with next/script. Covers App Router and Pages Router.',
relatedIds: ['react', 'vercel', 'nuxt'], relatedIds: ['react', 'vercel', 'nuxt'],
dedicatedPage: true,
}, },
{ {
id: 'react', id: 'react',
@@ -95,9 +94,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://react.dev', officialUrl: 'https://react.dev',
seoDescription:
'Integrate Pulse analytics with any React SPA — Create React App, Vite, or custom setups. Two easy methods.',
relatedIds: ['nextjs', 'remix', 'gatsby', 'preact'], relatedIds: ['nextjs', 'remix', 'gatsby', 'preact'],
dedicatedPage: true,
}, },
{ {
id: 'vue', id: 'vue',
@@ -111,9 +109,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://vuejs.org', officialUrl: 'https://vuejs.org',
seoDescription:
'Add Pulse privacy-first analytics to your Vue.js app. Works with Vue 2, Vue 3, Vue CLI, and Vite.',
relatedIds: ['nuxt', 'vitepress'], relatedIds: ['nuxt', 'vitepress'],
dedicatedPage: true,
}, },
{ {
id: 'angular', id: 'angular',
@@ -128,9 +125,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://angular.dev', officialUrl: 'https://angular.dev',
seoDescription:
'Add Pulse analytics to your Angular application. Simple index.html setup for all Angular versions.',
relatedIds: ['react', 'vue'], relatedIds: ['react', 'vue'],
dedicatedPage: true,
}, },
{ {
id: 'svelte', id: 'svelte',
@@ -144,9 +140,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://svelte.dev', officialUrl: 'https://svelte.dev',
seoDescription:
'Add Pulse analytics to Svelte or SvelteKit. Simple setup for both Vite-based Svelte and SvelteKit applications.',
relatedIds: ['astro', 'vue'], relatedIds: ['astro', 'vue'],
dedicatedPage: true,
}, },
{ {
id: 'nuxt', id: 'nuxt',
@@ -160,9 +155,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://nuxt.com/docs', officialUrl: 'https://nuxt.com/docs',
seoDescription:
'Configure Pulse analytics in Nuxt 2 or Nuxt 3 via nuxt.config. Simple, framework-native setup.',
relatedIds: ['vue', 'nextjs', 'vitepress'], relatedIds: ['vue', 'nextjs', 'vitepress'],
dedicatedPage: true,
}, },
{ {
id: 'remix', id: 'remix',
@@ -177,9 +171,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://remix.run/docs', officialUrl: 'https://remix.run/docs',
seoDescription:
'Add Pulse analytics to your Remix application via the root route. Simple script tag in app/root.tsx.',
relatedIds: ['react', 'nextjs'], relatedIds: ['react', 'nextjs'],
dedicatedPage: true,
}, },
{ {
id: 'astro', id: 'astro',
@@ -193,9 +186,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://docs.astro.build', officialUrl: 'https://docs.astro.build',
seoDescription:
'Integrate Pulse analytics with Astro. Add the script to your base layout for all pages.',
relatedIds: ['svelte', 'hugo', 'eleventy'], relatedIds: ['svelte', 'hugo', 'eleventy'],
dedicatedPage: true,
}, },
{ {
id: 'solidjs', id: 'solidjs',
@@ -209,9 +201,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://www.solidjs.com/docs', officialUrl: 'https://www.solidjs.com/docs',
seoDescription:
'Add Pulse analytics to your Solid.js application. Simple index.html script tag setup.',
relatedIds: ['react', 'qwik', 'preact'], relatedIds: ['react', 'qwik', 'preact'],
dedicatedPage: false,
}, },
{ {
id: 'qwik', id: 'qwik',
@@ -225,9 +216,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://qwik.dev/docs', officialUrl: 'https://qwik.dev/docs',
seoDescription:
'Integrate Pulse analytics with Qwik. Add the script to your root entry file.',
relatedIds: ['react', 'solidjs', 'astro'], relatedIds: ['react', 'solidjs', 'astro'],
dedicatedPage: false,
}, },
{ {
id: 'preact', id: 'preact',
@@ -241,9 +231,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://preactjs.com/guide', officialUrl: 'https://preactjs.com/guide',
seoDescription:
'Add Pulse analytics to your Preact application. Same simple setup as any Vite or HTML project.',
relatedIds: ['react', 'solidjs'], relatedIds: ['react', 'solidjs'],
dedicatedPage: false,
}, },
{ {
id: 'htmx', id: 'htmx',
@@ -257,9 +246,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://htmx.org/docs', officialUrl: 'https://htmx.org/docs',
seoDescription:
'Add Pulse analytics to your HTMX-powered site. Works with any backend serving HTML.',
relatedIds: ['django', 'flask', 'laravel', 'rails'], relatedIds: ['django', 'flask', 'laravel', 'rails'],
dedicatedPage: false,
}, },
{ {
id: 'ember', id: 'ember',
@@ -273,9 +261,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://guides.emberjs.com', officialUrl: 'https://guides.emberjs.com',
seoDescription:
'Add Pulse analytics to your Ember.js app. Simple index.html script tag setup.',
relatedIds: ['react', 'angular'], relatedIds: ['react', 'angular'],
dedicatedPage: false,
}, },
// * ─── Backend Frameworks ─────────────────────────────────────────────────── // * ─── Backend Frameworks ───────────────────────────────────────────────────
@@ -291,9 +278,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://laravel.com/docs', officialUrl: 'https://laravel.com/docs',
seoDescription:
'Add Pulse analytics to your Laravel app. Blade template integration for all Laravel versions.',
relatedIds: ['django', 'rails', 'wordpress'], relatedIds: ['django', 'rails', 'wordpress'],
dedicatedPage: true,
}, },
{ {
id: 'django', id: 'django',
@@ -307,9 +293,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://docs.djangoproject.com', officialUrl: 'https://docs.djangoproject.com',
seoDescription:
'Add Pulse analytics to your Django app. Template-based integration for all Django versions.',
relatedIds: ['flask', 'laravel', 'htmx'], relatedIds: ['flask', 'laravel', 'htmx'],
dedicatedPage: true,
}, },
{ {
id: 'rails', id: 'rails',
@@ -323,9 +308,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://guides.rubyonrails.org', officialUrl: 'https://guides.rubyonrails.org',
seoDescription:
'Add Pulse analytics to your Ruby on Rails app. ERB layout integration.',
relatedIds: ['laravel', 'django', 'jekyll'], relatedIds: ['laravel', 'django', 'jekyll'],
dedicatedPage: true,
}, },
{ {
id: 'flask', id: 'flask',
@@ -339,9 +323,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://flask.palletsprojects.com', officialUrl: 'https://flask.palletsprojects.com',
seoDescription:
'Add Pulse analytics to your Flask app. Jinja2 template integration.',
relatedIds: ['django', 'htmx', 'express'], relatedIds: ['django', 'htmx', 'express'],
dedicatedPage: true,
}, },
{ {
id: 'express', id: 'express',
@@ -356,9 +339,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://expressjs.com', officialUrl: 'https://expressjs.com',
seoDescription:
'Serve Pulse analytics from your Express.js app. Middleware or template-based setup.',
relatedIds: ['flask', 'nextjs', 'react'], relatedIds: ['flask', 'nextjs', 'react'],
dedicatedPage: true,
}, },
// * ─── Static Sites & Documentation ───────────────────────────────────────── // * ─── Static Sites & Documentation ─────────────────────────────────────────
@@ -374,9 +356,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://www.gatsbyjs.com/docs', officialUrl: 'https://www.gatsbyjs.com/docs',
seoDescription:
'Add Pulse analytics to your Gatsby site using gatsby-ssr or the Gatsby Head API.',
relatedIds: ['react', 'nextjs', 'hugo'], relatedIds: ['react', 'nextjs', 'hugo'],
dedicatedPage: true,
}, },
{ {
id: 'hugo', id: 'hugo',
@@ -390,9 +371,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://gohugo.io/documentation', officialUrl: 'https://gohugo.io/documentation',
seoDescription:
'Add Pulse analytics to your Hugo site via a partial or base template.',
relatedIds: ['jekyll', 'eleventy', 'astro'], relatedIds: ['jekyll', 'eleventy', 'astro'],
dedicatedPage: true,
}, },
{ {
id: 'eleventy', id: 'eleventy',
@@ -407,9 +387,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://www.11ty.dev/docs', officialUrl: 'https://www.11ty.dev/docs',
seoDescription:
'Add Pulse analytics to your Eleventy (11ty) site. Template-based integration.',
relatedIds: ['hugo', 'jekyll', 'astro'], relatedIds: ['hugo', 'jekyll', 'astro'],
dedicatedPage: true,
}, },
{ {
id: 'jekyll', id: 'jekyll',
@@ -423,9 +402,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://jekyllrb.com/docs', officialUrl: 'https://jekyllrb.com/docs',
seoDescription:
'Add Pulse analytics to your Jekyll site. Liquid template integration for GitHub Pages and beyond.',
relatedIds: ['hugo', 'eleventy', 'github-pages'], relatedIds: ['hugo', 'eleventy', 'github-pages'],
dedicatedPage: false,
}, },
{ {
id: 'docusaurus', id: 'docusaurus',
@@ -439,9 +417,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://docusaurus.io/docs', officialUrl: 'https://docusaurus.io/docs',
seoDescription:
'Add Pulse analytics to your Docusaurus documentation site via docusaurus.config.js.',
relatedIds: ['vitepress', 'mkdocs', 'gatsby'], relatedIds: ['vitepress', 'mkdocs', 'gatsby'],
dedicatedPage: false,
}, },
{ {
id: 'vitepress', id: 'vitepress',
@@ -455,9 +432,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://vitepress.dev', officialUrl: 'https://vitepress.dev',
seoDescription:
'Add Pulse analytics to your VitePress documentation site via config.',
relatedIds: ['docusaurus', 'vue', 'nuxt'], relatedIds: ['docusaurus', 'vue', 'nuxt'],
dedicatedPage: false,
}, },
{ {
id: 'hexo', id: 'hexo',
@@ -471,9 +447,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://hexo.io/docs', officialUrl: 'https://hexo.io/docs',
seoDescription:
'Add Pulse analytics to your Hexo blog or documentation site.',
relatedIds: ['hugo', 'jekyll', 'eleventy'], relatedIds: ['hugo', 'jekyll', 'eleventy'],
dedicatedPage: false,
}, },
{ {
id: 'mkdocs', id: 'mkdocs',
@@ -487,9 +462,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://www.mkdocs.org', officialUrl: 'https://www.mkdocs.org',
seoDescription:
'Add Pulse analytics to your MkDocs documentation site.',
relatedIds: ['docusaurus', 'vitepress', 'django'], relatedIds: ['docusaurus', 'vitepress', 'django'],
dedicatedPage: false,
}, },
// * ─── CMS & Blogging ────────────────────────────────────────────────────── // * ─── CMS & Blogging ──────────────────────────────────────────────────────
@@ -505,9 +479,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://wordpress.org/documentation', officialUrl: 'https://wordpress.org/documentation',
seoDescription:
'Add Pulse analytics to your WordPress site via a plugin or theme header code.',
relatedIds: ['ghost', 'drupal', 'woocommerce'], relatedIds: ['ghost', 'drupal', 'woocommerce'],
dedicatedPage: true,
}, },
{ {
id: 'ghost', id: 'ghost',
@@ -522,9 +495,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://ghost.org/docs', officialUrl: 'https://ghost.org/docs',
seoDescription:
'Add Pulse analytics to your Ghost blog via Code Injection settings.',
relatedIds: ['wordpress', 'blogger'], relatedIds: ['wordpress', 'blogger'],
dedicatedPage: true,
}, },
{ {
id: 'drupal', id: 'drupal',
@@ -538,9 +510,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://www.drupal.org/docs', officialUrl: 'https://www.drupal.org/docs',
seoDescription:
'Add Pulse analytics to your Drupal site using a module or theme template.',
relatedIds: ['wordpress', 'joomla'], relatedIds: ['wordpress', 'joomla'],
dedicatedPage: true,
}, },
{ {
id: 'joomla', id: 'joomla',
@@ -554,9 +525,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://docs.joomla.org', officialUrl: 'https://docs.joomla.org',
seoDescription:
'Add Pulse analytics to your Joomla site via template or extension.',
relatedIds: ['wordpress', 'drupal'], relatedIds: ['wordpress', 'drupal'],
dedicatedPage: false,
}, },
{ {
id: 'strapi', id: 'strapi',
@@ -570,9 +540,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://docs.strapi.io', officialUrl: 'https://docs.strapi.io',
seoDescription:
'Add Pulse analytics to your Strapi-powered frontend.',
relatedIds: ['contentful', 'sanity', 'nextjs'], relatedIds: ['contentful', 'sanity', 'nextjs'],
dedicatedPage: false,
}, },
{ {
id: 'sanity', id: 'sanity',
@@ -587,9 +556,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://www.sanity.io/docs', officialUrl: 'https://www.sanity.io/docs',
seoDescription:
'Add Pulse analytics to your Sanity-powered frontend application.',
relatedIds: ['strapi', 'contentful', 'nextjs'], relatedIds: ['strapi', 'contentful', 'nextjs'],
dedicatedPage: false,
}, },
{ {
id: 'contentful', id: 'contentful',
@@ -603,9 +571,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://www.contentful.com/developers/docs', officialUrl: 'https://www.contentful.com/developers/docs',
seoDescription:
'Add Pulse analytics to your Contentful-powered frontend.',
relatedIds: ['strapi', 'sanity', 'nextjs'], relatedIds: ['strapi', 'sanity', 'nextjs'],
dedicatedPage: false,
}, },
{ {
id: 'payload', id: 'payload',
@@ -620,9 +587,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://payloadcms.com/docs', officialUrl: 'https://payloadcms.com/docs',
seoDescription:
'Add Pulse analytics to your Payload CMS frontend application.',
relatedIds: ['strapi', 'contentful', 'nextjs'], relatedIds: ['strapi', 'contentful', 'nextjs'],
dedicatedPage: false,
}, },
// * ─── eCommerce ──────────────────────────────────────────────────────────── // * ─── eCommerce ────────────────────────────────────────────────────────────
@@ -638,9 +604,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://shopify.dev/docs', officialUrl: 'https://shopify.dev/docs',
seoDescription:
'Add Pulse privacy-first analytics to your Shopify store via the theme editor.',
relatedIds: ['woocommerce', 'bigcommerce', 'prestashop'], relatedIds: ['woocommerce', 'bigcommerce', 'prestashop'],
dedicatedPage: true,
}, },
{ {
id: 'woocommerce', id: 'woocommerce',
@@ -654,9 +619,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://woocommerce.com/documentation', officialUrl: 'https://woocommerce.com/documentation',
seoDescription:
'Add Pulse analytics to your WooCommerce store. WordPress-based setup.',
relatedIds: ['shopify', 'wordpress', 'bigcommerce'], relatedIds: ['shopify', 'wordpress', 'bigcommerce'],
dedicatedPage: false,
}, },
{ {
id: 'bigcommerce', id: 'bigcommerce',
@@ -671,9 +635,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://developer.bigcommerce.com/docs', officialUrl: 'https://developer.bigcommerce.com/docs',
seoDescription:
'Add Pulse analytics to your BigCommerce store via Script Manager.',
relatedIds: ['shopify', 'woocommerce', 'prestashop'], relatedIds: ['shopify', 'woocommerce', 'prestashop'],
dedicatedPage: false,
}, },
{ {
id: 'prestashop', id: 'prestashop',
@@ -687,9 +650,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://devdocs.prestashop-project.org', officialUrl: 'https://devdocs.prestashop-project.org',
seoDescription:
'Add Pulse analytics to your PrestaShop store via theme template.',
relatedIds: ['shopify', 'woocommerce', 'bigcommerce'], relatedIds: ['shopify', 'woocommerce', 'bigcommerce'],
dedicatedPage: false,
}, },
// * ─── Platforms & Tools ──────────────────────────────────────────────────── // * ─── Platforms & Tools ────────────────────────────────────────────────────
@@ -705,9 +667,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://university.webflow.com', officialUrl: 'https://university.webflow.com',
seoDescription:
'Add Pulse analytics to your Webflow site via project custom code settings.',
relatedIds: ['squarespace', 'wix', 'framer'], relatedIds: ['squarespace', 'wix', 'framer'],
dedicatedPage: true,
}, },
{ {
id: 'squarespace', id: 'squarespace',
@@ -722,9 +683,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://support.squarespace.com', officialUrl: 'https://support.squarespace.com',
seoDescription:
'Add Pulse analytics to your Squarespace site via the Code Injection panel.',
relatedIds: ['webflow', 'wix', 'carrd'], relatedIds: ['webflow', 'wix', 'carrd'],
dedicatedPage: true,
}, },
{ {
id: 'wix', id: 'wix',
@@ -738,9 +698,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://support.wix.com', officialUrl: 'https://support.wix.com',
seoDescription:
'Add Pulse analytics to your Wix site via Custom Code settings.',
relatedIds: ['webflow', 'squarespace', 'framer'], relatedIds: ['webflow', 'squarespace', 'framer'],
dedicatedPage: true,
}, },
{ {
id: 'framer', id: 'framer',
@@ -754,9 +713,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://www.framer.com/help', officialUrl: 'https://www.framer.com/help',
seoDescription:
'Add Pulse analytics to your Framer site via custom code settings.',
relatedIds: ['webflow', 'squarespace', 'wix'], relatedIds: ['webflow', 'squarespace', 'wix'],
dedicatedPage: true,
}, },
{ {
id: 'carrd', id: 'carrd',
@@ -770,9 +728,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://carrd.co/docs', officialUrl: 'https://carrd.co/docs',
seoDescription:
'Add Pulse analytics to your Carrd one-page site via head code.',
relatedIds: ['framer', 'webflow'], relatedIds: ['framer', 'webflow'],
dedicatedPage: false,
}, },
{ {
id: 'blogger', id: 'blogger',
@@ -786,9 +743,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://support.google.com/blogger', officialUrl: 'https://support.google.com/blogger',
seoDescription:
'Add Pulse analytics to your Blogger blog via theme HTML editor.',
relatedIds: ['wordpress', 'ghost'], relatedIds: ['wordpress', 'ghost'],
dedicatedPage: false,
}, },
{ {
id: 'gtm', id: 'gtm',
@@ -802,9 +758,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://tagmanager.google.com', officialUrl: 'https://tagmanager.google.com',
seoDescription:
'Add Pulse analytics via Google Tag Manager. Works with any site using GTM.',
relatedIds: ['wordpress', 'shopify', 'webflow'], relatedIds: ['wordpress', 'shopify', 'webflow'],
dedicatedPage: true,
}, },
{ {
id: 'notion', id: 'notion',
@@ -819,9 +774,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://www.notion.so', officialUrl: 'https://www.notion.so',
seoDescription:
'Add Pulse analytics to Notion-powered websites using Super.so, Potion, or similar tools.',
relatedIds: ['webflow', 'framer', 'carrd'], relatedIds: ['webflow', 'framer', 'carrd'],
dedicatedPage: false,
}, },
// * ─── Hosting & Deployment ───────────────────────────────────────────────── // * ─── Hosting & Deployment ─────────────────────────────────────────────────
@@ -837,9 +791,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://developers.cloudflare.com/pages', officialUrl: 'https://developers.cloudflare.com/pages',
seoDescription:
'Deploy with Pulse analytics on Cloudflare Pages. Works with any framework.',
relatedIds: ['netlify', 'vercel', 'github-pages'], relatedIds: ['netlify', 'vercel', 'github-pages'],
dedicatedPage: false,
}, },
{ {
id: 'netlify', id: 'netlify',
@@ -853,9 +806,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://docs.netlify.com', officialUrl: 'https://docs.netlify.com',
seoDescription:
'Add Pulse analytics to sites deployed on Netlify. Snippet injection and build-based methods.',
relatedIds: ['cloudflare-pages', 'vercel', 'github-pages'], relatedIds: ['cloudflare-pages', 'vercel', 'github-pages'],
dedicatedPage: false,
}, },
{ {
id: 'vercel', id: 'vercel',
@@ -870,9 +822,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://vercel.com/docs', officialUrl: 'https://vercel.com/docs',
seoDescription:
'Add Pulse analytics to sites deployed on Vercel. Works with any framework.',
relatedIds: ['netlify', 'cloudflare-pages', 'nextjs'], relatedIds: ['netlify', 'cloudflare-pages', 'nextjs'],
dedicatedPage: false,
}, },
{ {
id: 'github-pages', id: 'github-pages',
@@ -887,9 +838,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://docs.github.com/en/pages', officialUrl: 'https://docs.github.com/en/pages',
seoDescription:
'Add Pulse analytics to your GitHub Pages site. Works with Jekyll, Hugo, or plain HTML.',
relatedIds: ['jekyll', 'hugo', 'netlify'], relatedIds: ['jekyll', 'hugo', 'netlify'],
dedicatedPage: false,
}, },
// * ─── CMS & Blogging (continued) ────────────────────────────────────────── // * ─── CMS & Blogging (continued) ──────────────────────────────────────────
@@ -905,9 +855,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://craftcms.com/docs', officialUrl: 'https://craftcms.com/docs',
seoDescription:
'Add Pulse analytics to your Craft CMS site via Twig templates. Simple installation.',
relatedIds: ['wordpress', 'statamic', 'drupal'], relatedIds: ['wordpress', 'statamic', 'drupal'],
dedicatedPage: false,
}, },
{ {
id: 'statamic', id: 'statamic',
@@ -921,9 +870,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://statamic.dev/docs', officialUrl: 'https://statamic.dev/docs',
seoDescription:
'Add Pulse analytics to your Statamic site. Antlers template integration.',
relatedIds: ['craftcms', 'laravel', 'wordpress'], relatedIds: ['craftcms', 'laravel', 'wordpress'],
dedicatedPage: false,
}, },
{ {
id: 'typo3', id: 'typo3',
@@ -937,9 +885,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://docs.typo3.org', officialUrl: 'https://docs.typo3.org',
seoDescription:
'Add Pulse analytics to your TYPO3 CMS site. TypoScript and Fluid template integration.',
relatedIds: ['wordpress', 'drupal', 'joomla'], relatedIds: ['wordpress', 'drupal', 'joomla'],
dedicatedPage: false,
}, },
{ {
id: 'kirby', id: 'kirby',
@@ -954,9 +901,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://getkirby.com/docs', officialUrl: 'https://getkirby.com/docs',
seoDescription:
'Add Pulse analytics to your Kirby CMS site. Simple PHP snippet integration.',
relatedIds: ['craftcms', 'statamic', 'grav'], relatedIds: ['craftcms', 'statamic', 'grav'],
dedicatedPage: false,
}, },
{ {
id: 'grav', id: 'grav',
@@ -971,9 +917,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://learn.getgrav.org', officialUrl: 'https://learn.getgrav.org',
seoDescription:
'Add Pulse analytics to your Grav flat-file CMS. Twig template integration.',
relatedIds: ['kirby', 'craftcms', 'hugo'], relatedIds: ['kirby', 'craftcms', 'hugo'],
dedicatedPage: false,
}, },
{ {
id: 'umbraco', id: 'umbraco',
@@ -987,9 +932,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://docs.umbraco.com', officialUrl: 'https://docs.umbraco.com',
seoDescription:
'Add Pulse analytics to your Umbraco CMS site. Razor view integration for .NET.',
relatedIds: ['wordpress', 'drupal', 'typo3'], relatedIds: ['wordpress', 'drupal', 'typo3'],
dedicatedPage: false,
}, },
{ {
id: 'storyblok', id: 'storyblok',
@@ -1003,9 +947,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://www.storyblok.com/docs', officialUrl: 'https://www.storyblok.com/docs',
seoDescription:
'Add Pulse analytics to your Storyblok-powered frontend application.',
relatedIds: ['contentful', 'prismic', 'nextjs'], relatedIds: ['contentful', 'prismic', 'nextjs'],
dedicatedPage: false,
}, },
{ {
id: 'prismic', id: 'prismic',
@@ -1019,9 +962,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://prismic.io/docs', officialUrl: 'https://prismic.io/docs',
seoDescription:
'Add Pulse analytics to your Prismic-powered frontend application.',
relatedIds: ['contentful', 'storyblok', 'nextjs'], relatedIds: ['contentful', 'storyblok', 'nextjs'],
dedicatedPage: false,
}, },
// * ─── eCommerce (continued) ─────────────────────────────────────────────── // * ─── eCommerce (continued) ───────────────────────────────────────────────
@@ -1037,9 +979,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://developer.shopware.com/docs', officialUrl: 'https://developer.shopware.com/docs',
seoDescription:
'Add Pulse analytics to your Shopware 6 store via theme template.',
relatedIds: ['shopify', 'woocommerce', 'magento'], relatedIds: ['shopify', 'woocommerce', 'magento'],
dedicatedPage: false,
}, },
{ {
id: 'magento', id: 'magento',
@@ -1053,9 +994,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://developer.adobe.com/commerce', officialUrl: 'https://developer.adobe.com/commerce',
seoDescription:
'Add Pulse analytics to your Magento or Adobe Commerce store via layout XML.',
relatedIds: ['shopify', 'woocommerce', 'shopware'], relatedIds: ['shopify', 'woocommerce', 'shopware'],
dedicatedPage: false,
}, },
// * ─── Platforms & Tools (continued) ─────────────────────────────────────── // * ─── Platforms & Tools (continued) ───────────────────────────────────────
@@ -1072,9 +1012,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://manual.bubble.io', officialUrl: 'https://manual.bubble.io',
seoDescription:
'Add Pulse analytics to your Bubble no-code app via the SEO/Meta tags section.',
relatedIds: ['webflow', 'framer', 'wix'], relatedIds: ['webflow', 'framer', 'wix'],
dedicatedPage: false,
}, },
{ {
id: 'discourse', id: 'discourse',
@@ -1089,9 +1028,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://meta.discourse.org/docs', officialUrl: 'https://meta.discourse.org/docs',
seoDescription:
'Add Pulse analytics to your Discourse forum via admin customization.',
relatedIds: ['wordpress', 'ghost'], relatedIds: ['wordpress', 'ghost'],
dedicatedPage: false,
}, },
{ {
id: 'hubspot', id: 'hubspot',
@@ -1105,9 +1043,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://knowledge.hubspot.com', officialUrl: 'https://knowledge.hubspot.com',
seoDescription:
'Add Pulse analytics to HubSpot landing pages and website via Settings.',
relatedIds: ['wordpress', 'webflow'], relatedIds: ['wordpress', 'webflow'],
dedicatedPage: false,
}, },
{ {
id: 'substack', id: 'substack',
@@ -1121,9 +1058,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://substack.com', officialUrl: 'https://substack.com',
seoDescription:
'Add Pulse analytics to your Substack publication using custom domain settings.',
relatedIds: ['ghost', 'blogger', 'wordpress'], relatedIds: ['ghost', 'blogger', 'wordpress'],
dedicatedPage: false,
}, },
{ {
id: 'linktree', id: 'linktree',
@@ -1137,9 +1073,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://linktr.ee', officialUrl: 'https://linktr.ee',
seoDescription:
'Add Pulse analytics to your Linktree link-in-bio page.',
relatedIds: ['carrd', 'framer', 'webflow'], relatedIds: ['carrd', 'framer', 'webflow'],
dedicatedPage: false,
}, },
{ {
id: 'weebly', id: 'weebly',
@@ -1153,9 +1088,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://www.weebly.com', officialUrl: 'https://www.weebly.com',
seoDescription:
'Add Pulse analytics to your Weebly website via the header code settings.',
relatedIds: ['squarespace', 'wix', 'webflow'], relatedIds: ['squarespace', 'wix', 'webflow'],
dedicatedPage: false,
}, },
// * ─── Static Sites & Documentation (continued) ─────────────────────────── // * ─── Static Sites & Documentation (continued) ───────────────────────────
@@ -1171,9 +1105,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://docs.gitbook.com', officialUrl: 'https://docs.gitbook.com',
seoDescription:
'Add Pulse analytics to your GitBook-hosted documentation.',
relatedIds: ['docusaurus', 'readme', 'readthedocs'], relatedIds: ['docusaurus', 'readme', 'readthedocs'],
dedicatedPage: false,
}, },
{ {
id: 'gridsome', id: 'gridsome',
@@ -1187,9 +1120,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://gridsome.org/docs', officialUrl: 'https://gridsome.org/docs',
seoDescription:
'Add Pulse analytics to your Gridsome Vue-based static site.',
relatedIds: ['gatsby', 'vue', 'nuxt'], relatedIds: ['gatsby', 'vue', 'nuxt'],
dedicatedPage: false,
}, },
{ {
id: 'readthedocs', id: 'readthedocs',
@@ -1204,9 +1136,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://docs.readthedocs.io', officialUrl: 'https://docs.readthedocs.io',
seoDescription:
'Add Pulse analytics to your Read the Docs documentation site.',
relatedIds: ['sphinx', 'mkdocs', 'docusaurus'], relatedIds: ['sphinx', 'mkdocs', 'docusaurus'],
dedicatedPage: false,
}, },
{ {
id: 'sphinx', id: 'sphinx',
@@ -1221,9 +1152,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://www.sphinx-doc.org', officialUrl: 'https://www.sphinx-doc.org',
seoDescription:
'Add Pulse analytics to your Sphinx-generated documentation.',
relatedIds: ['readthedocs', 'mkdocs', 'docusaurus'], relatedIds: ['readthedocs', 'mkdocs', 'docusaurus'],
dedicatedPage: false,
}, },
{ {
id: 'readme', id: 'readme',
@@ -1237,9 +1167,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://docs.readme.com', officialUrl: 'https://docs.readme.com',
seoDescription:
'Add Pulse analytics to your ReadMe API documentation portal.',
relatedIds: ['gitbook', 'docusaurus', 'readthedocs'], relatedIds: ['gitbook', 'docusaurus', 'readthedocs'],
dedicatedPage: false,
}, },
// * ─── JavaScript Frameworks (continued) ─────────────────────────────────── // * ─── JavaScript Frameworks (continued) ───────────────────────────────────
@@ -1255,9 +1184,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://docs.flutter.dev', officialUrl: 'https://docs.flutter.dev',
seoDescription:
'Add Pulse analytics to your Flutter web app via web/index.html.',
relatedIds: ['react', 'angular', 'preact'], relatedIds: ['react', 'angular', 'preact'],
dedicatedPage: false,
}, },
// * ─── Hosting & Deployment (continued) ──────────────────────────────────── // * ─── Hosting & Deployment (continued) ────────────────────────────────────
@@ -1274,9 +1202,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://docs.render.com', officialUrl: 'https://docs.render.com',
seoDescription:
'Add Pulse analytics to sites deployed on Render. Works with any framework.',
relatedIds: ['netlify', 'vercel', 'cloudflare-pages'], relatedIds: ['netlify', 'vercel', 'cloudflare-pages'],
dedicatedPage: false,
}, },
{ {
id: 'firebase', id: 'firebase',
@@ -1290,9 +1217,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://firebase.google.com/docs/hosting', officialUrl: 'https://firebase.google.com/docs/hosting',
seoDescription:
'Add Pulse analytics to sites deployed on Firebase Hosting.',
relatedIds: ['netlify', 'vercel', 'render'], relatedIds: ['netlify', 'vercel', 'render'],
dedicatedPage: false,
}, },
// * ─── Platforms & Tools (continued) ─────────────────────────────────────── // * ─── Platforms & Tools (continued) ───────────────────────────────────────
@@ -1308,9 +1234,8 @@ export const integrations: Integration[] = [
</svg> </svg>
), ),
officialUrl: 'https://amp.dev/documentation', officialUrl: 'https://amp.dev/documentation',
seoDescription:
'Add Pulse analytics to Google AMP pages using amp-analytics.',
relatedIds: ['gtm', 'wordpress', 'webflow'], relatedIds: ['gtm', 'wordpress', 'webflow'],
dedicatedPage: false,
}, },
] ]

View File

@@ -76,12 +76,31 @@ const nextConfig: NextConfig = {
] ]
}, },
async redirects() { async redirects() {
const removedIntegrations = [
'solidjs', 'qwik', 'preact', 'htmx', 'ember',
'jekyll', 'docusaurus', 'vitepress', 'hexo', 'mkdocs',
'joomla', 'strapi', 'sanity', 'contentful', 'payload',
'craftcms', 'statamic', 'typo3', 'kirby', 'grav', 'umbraco',
'storyblok', 'prismic', 'shopware', 'magento',
'woocommerce', 'bigcommerce', 'prestashop',
'blogger', 'substack', 'linktree', 'weebly', 'gitbook',
'gridsome', 'readthedocs', 'sphinx', 'readme',
'bubble', 'discourse', 'hubspot', 'notion',
'cloudflare-pages', 'netlify', 'vercel', 'github-pages',
'firebase', 'render', 'flutter', 'amp', 'carrd',
]
return [ return [
{ {
source: '/dashboard', source: '/dashboard',
destination: '/', destination: '/',
permanent: false, permanent: false,
}, },
...removedIntegrations.map((slug) => ({
source: `/integrations/${slug}`,
destination: '/integrations/script-tag',
permanent: true,
})),
] ]
}, },
async rewrites() { async rewrites() {

1010
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,8 @@
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"generate:integrations": "npx tsx scripts/generate-integrations.ts",
"prebuild": "npm run generate:integrations",
"build": "next build --webpack", "build": "next build --webpack",
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "next lint",
@@ -37,18 +39,21 @@
"d3-array": "^3.2.4", "d3-array": "^3.2.4",
"d3-scale": "^4.0.2", "d3-scale": "^4.0.2",
"framer-motion": "^12.23.26", "framer-motion": "^12.23.26",
"gray-matter": "^4.0.3",
"html-to-image": "^1.11.13", "html-to-image": "^1.11.13",
"iso-3166-2": "^1.0.0", "iso-3166-2": "^1.0.0",
"jspdf": "^4.0.0", "jspdf": "^4.0.0",
"jspdf-autotable": "^5.0.7", "jspdf-autotable": "^5.0.7",
"lucide-react": "^0.577.0", "lucide-react": "^0.577.0",
"next": "^16.1.1", "next": "^16.1.1",
"next-mdx-remote": "^6.0.0",
"radix-ui": "^1.4.3", "radix-ui": "^1.4.3",
"react": "^19.2.3", "react": "^19.2.3",
"react-dom": "^19.2.3", "react-dom": "^19.2.3",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
"react-use-measure": "^2.1.7", "react-use-measure": "^2.1.7",
"recharts": "^2.15.0", "recharts": "^2.15.0",
"remark-gfm": "^4.0.1",
"sonner": "^2.0.7", "sonner": "^2.0.7",
"svg-dotted-map": "^2.0.1", "svg-dotted-map": "^2.0.1",
"swr": "^2.3.3", "swr": "^2.3.3",

View File

@@ -0,0 +1,44 @@
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
const CONTENT_DIR = path.join(process.cwd(), 'content', 'integrations')
const OUTPUT_PATH = path.join(process.cwd(), 'lib', 'integration-guides.gen.ts')
const files = fs.existsSync(CONTENT_DIR)
? fs.readdirSync(CONTENT_DIR).filter((f) => f.endsWith('.mdx'))
: []
const guides: { slug: string; title: string; description: string; category: string; date: string }[] = []
for (const filename of files) {
const slug = filename.replace(/\.mdx$/, '')
const raw = fs.readFileSync(path.join(CONTENT_DIR, filename), 'utf-8')
const { data } = matter(raw)
guides.push({
slug,
title: data.title as string,
description: data.description as string,
category: data.category as string,
date: data.date as string,
})
}
guides.sort((a, b) => a.title.localeCompare(b.title))
const output = `// Auto-generated from content/integrations/*.mdx — do not edit manually
// Run: npm run generate:integrations
export interface IntegrationGuideSummary {
slug: string
title: string
description: string
category: string
date: string
}
export const integrationGuides: IntegrationGuideSummary[] = ${JSON.stringify(guides, null, 2)}
`
fs.writeFileSync(OUTPUT_PATH, output, 'utf-8')
console.log(`Generated ${guides.length} integration guides → lib/integration-guides.gen.ts`)