documentation

blockrate docs

A 1.6 KB client library that measures per-provider block rate. Drop it in, point it at blockrate.app or your own server, see exactly how much each third-party tool is being blocked.

Quick start

terminal
1bun add blockrate

The library runs in the browser, checks each provider, and calls your reporter with the results. There are two ways to collect data:

Option A: Hosted dashboard (blockrate.app)

Sign up, get an API key from /app/keys, and use serverReporter. No server code needed — blockrate.app stores and visualizes the data.

app.tsx
1import { BlockRate, serverReporter } from "blockrate";
2
3new BlockRate({
4 providers: ["optimizely", "posthog", "ga4"],
5 service: "my-app",
6 reporter: serverReporter({
7 endpoint: "https://blockrate.app/api",
8 apiKey: "br_your_key_here",
9 }),
10 sampleRate: 0.1, // check 10% of sessions
11}).check();

Option B: Self-hosted (blockrate-server)

Run blockrate-server on your own infrastructure. Same library, different endpoint.

app.tsx
1import { BlockRate, serverReporter } from "blockrate";
2
3new BlockRate({
4 providers: ["optimizely", "posthog", "ga4"],
5 reporter: serverReporter({
6 endpoint: "https://br.your-domain.com",
7 apiKey: "br_your_self_hosted_key",
8 }),
9}).check();

Option C: Custom pipeline

Write your own reporter to forward results anywhere — BigQuery, Datadog, a webhook, your own API. The reporter receives a BlockRateResult object.

custom-reporter.ts
1import { BlockRate } from "blockrate";
2
3new BlockRate({
4 providers: ["optimizely", "posthog", "ga4"],
5 reporter: (result) => {
6 // result.providers = [{ name, status, latency }, ...]
7 fetch("/your-api/analytics", {
8 method: "POST",
9 body: JSON.stringify(result),
10 headers: { "Content-Type": "application/json" },
11 keepalive: true,
12 });
13 },
14}).check();

Built-in providers

Each provider is checked first via a window global (fast — the script already loaded), then via a fetch HEAD probe to its CDN with mode: "cors". If the ad blocker redirects to a local response (which lacks CORS headers), the fetch throws — correctly detected as blocked.

NameDetection
optimizelywindow.optimizely + cdn.optimizely.com probe
posthogwindow.posthog + us.i.posthog.com / eu.i.posthog.com probe
ga4window.gtag / dataLayer + google-analytics.com probe
gtmwindow.google_tag_manager + googletagmanager.com probe
segmentwindow.analytics + cdn.segment.com probe
hotjarwindow.hj + static.hotjar.com probe
amplitudewindow.amplitude + cdn.amplitude.com probe
mixpanelwindow.mixpanel + cdn.mxpnl.com probe
meta-pixelwindow.fbq + connect.facebook.net probe
intercomwindow.Intercom + widget.intercom.io probe

Need a provider we don't ship? Add your own:

custom-provider.ts
1import { BlockRate, createProvider, serverReporter } from "blockrate";
2
3const myProvider = createProvider({
4 name: "my-analytics",
5 detect: async () => (window.myAnalytics ? "loaded" : "blocked"),
6});
7
8new BlockRate({
9 providers: ["posthog", myProvider], // mix built-in + custom
10 reporter: serverReporter({ endpoint: "https://blockrate.app/api", apiKey: "..." }),
11}).check();

Framework guides

The library is framework-agnostic — it's just a class that calls .check(). These guides show the idiomatic way to wire it into each framework so it runs once per session, handles SSR safely, and doesn't block rendering.

React

The useBlockRate hook runs once on mount, skips on the server, and handles cleanup.

App.tsx
1import { useBlockRate } from "blockrate/react";
2import { serverReporter } from "blockrate";
3
4function App() {
5 useBlockRate({
6 providers: ["optimizely", "posthog", "ga4"],
7 reporter: serverReporter({
8 endpoint: "https://blockrate.app/api",
9 apiKey: process.env.NEXT_PUBLIC_BR_KEY!,
10 }),
11 sampleRate: 0.1,
12 });
13
14 return <div>...</div>;
15}

Next.js (App Router)

Add a client component that calls useBlockRate and drop it in your root layout. The "use client" directive is required because the library uses browser APIs.

components/blockrate.tsx
1"use client";
2
3import { useBlockRate } from "blockrate/react";
4import { serverReporter } from "blockrate";
5
6export function BlockRate() {
7 useBlockRate({
8 providers: ["optimizely", "posthog", "ga4"],
9 reporter: serverReporter({
10 endpoint: "https://blockrate.app/api",
11 apiKey: process.env.NEXT_PUBLIC_BR_KEY!,
12 }),
13 sampleRate: 0.1,
14 });
15 return null;
16}
app/layout.tsx
1import { BlockRate } from "@/components/blockrate";
2
3export default function RootLayout({ children }) {
4 return (
5 <html>
6 <body>
7 {children}
8 <BlockRate />
9 </body>
10 </html>
11 );
12}

SvelteKit

Call BlockRate in onMount in your root layout.

+layout.svelte
1<script lang="ts">
2 import { onMount } from "svelte";
3 import { BlockRate, serverReporter } from "blockrate";
4
5 onMount(() => {
6 new BlockRate({
7 providers: ["optimizely", "posthog", "ga4"],
8 reporter: serverReporter({
9 endpoint: "https://blockrate.app/api",
10 apiKey: import.meta.env.VITE_BR_KEY,
11 }),
12 sampleRate: 0.1,
13 }).check();
14 });
15</script>
16
17<slot />

TanStack Start

Same useBlockRate hook as React, dropped into the root layout component.

routes/__root.tsx
1import { useBlockRate } from "blockrate/react";
2import { serverReporter } from "blockrate";
3
4function RootLayout() {
5 useBlockRate({
6 providers: ["optimizely", "posthog", "ga4"],
7 reporter: serverReporter({
8 endpoint: "https://blockrate.app/api",
9 apiKey: import.meta.env.VITE_BR_KEY,
10 }),
11 sampleRate: 0.1,
12 });
13
14 return <Outlet />;
15}

Vanilla JS / script tag

Import the library directly in a script tag. Works in any site.

index.html
1<script type="module">
2 import { BlockRate, serverReporter } from "https://esm.sh/blockrate";
3
4 new BlockRate({
5 providers: ["optimizely", "posthog", "ga4"],
6 reporter: serverReporter({
7 endpoint: "https://blockrate.app/api",
8 apiKey: "br_your_key_here",
9 }),
10 sampleRate: 0.1,
11 }).check();
12</script>

Need the hosted API reference? See /docs/api · Self-hosting? See packages/server