We use cookies to enhance your experience on the site
CodeWorlds
Back to collections
Guide31 min read

Plausible

Plausible is privacy-friendly analytics without cookies and GDPR banners. Lightweight alternative to Google Analytics with self-hosting and open source.

Plausible - Privacy-First Analytics

Czym jest Plausible?

Plausible Analytics to lekka, open source'owa platforma analityczna stworzona jako alternatywa dla Google Analytics z myślą o prywatności użytkowników. Założona w 2019 roku w Estonii przez Uku Täht i Marko Saric, Plausible wyrosło z potrzeby posiadania narzędzia analitycznego, które nie wymaga cookies, bannerów zgody GDPR i nie zbiera danych osobowych.

W przeciwieństwie do tradycyjnych narzędzi analitycznych, które śledzą użytkowników przez całą sieć za pomocą cookies third-party, Plausible używa unikalnego podejścia opartego na hashowaniu adresów IP w połączeniu z User Agent. To oznacza, że każda wizyta jest anonimowa, a Plausible nie buduje profili użytkowników ani nie sprzedaje danych reklamodawcom.

Historia i filozofia Plausible

Plausible powstało jako odpowiedź na rosnące obawy dotyczące prywatności w internecie i coraz bardziej skomplikowane regulacje jak GDPR (Europa), CCPA (Kalifornia) czy LGPD (Brazylia). Twórcy zauważyli, że większość właścicieli stron potrzebuje tylko podstawowych metryk - ilu ludzi odwiedza stronę, skąd przychodzą i które strony są popularne.

Google Analytics oferuje tysiące metryk i wymiarów, ale większość z nich nie jest potrzebna zwykłym twórcom treści czy małym firmom. Za to wymaga akceptacji skomplikowanej polityki prywatności, instalacji bannerów cookie i potencjalnie naraża na kary za naruszenie GDPR sięgające 4% rocznego obrotu.

Plausible to filozofia "less is more" - prosty dashboard z najważniejszymi informacjami, bez zbędnego szumu i bez kompromisów w kwestii prywatności.

Dlaczego Plausible?

1. Brak cookies = Brak GDPR bannerów

Plausible nie używa cookies ani żadnej formy persistent storage. Nie musisz wyświetlać irytujących banerów z pytaniem o zgodę na cookies. Twoja strona ładuje się szybciej i wygląda profesjonalniej bez tych pop-upów.

2. Ekstremalnie lekki skrypt

Skrypt Plausible waży mniej niż 1KB (gzipped), podczas gdy Google Analytics to ponad 45KB. To oznacza:

  • Szybsze ładowanie strony
  • Lepszy Core Web Vitals score
  • Niższe zużycie transferu dla odwiedzających
  • Mniejszy ślad węglowy strony

3. Pełna zgodność z przepisami

Plausible jest w pełni zgodny z:

  • GDPR (Europa)
  • CCPA (Kalifornia)
  • PECR (UK)
  • LGPD (Brazylia)
  • PIPEDA (Kanada)

Dane są przechowywane na serwerach w Unii Europejskiej, co jest wymogiem wielu europejskich firm i instytucji.

4. Open Source i Self-hosting

Plausible jest w pełni open source (licencja AGPL). Możesz:

  • Zobaczyć dokładnie, jak działa kod
  • Uruchomić własną instancję na swoich serwerach
  • Mieć 100% kontrolę nad danymi
  • Nigdy nie martwić się o vendor lock-in

5. Prosty, czytelny dashboard

Zamiast setek raportów i wymiarów, dostajesz jeden elegancki dashboard z najważniejszymi informacjami:

  • Liczba odwiedzających (unique visitors)
  • Odsłony (pageviews)
  • Współczynnik odrzuceń (bounce rate)
  • Czas na stronie
  • Źródła ruchu
  • Popularne strony
  • Lokalizacje geograficzne
  • Urządzenia i przeglądarki

6. Etyczne podejście do biznesu

Plausible to bootstrapped firma bez zewnętrznych inwestorów. To oznacza:

  • Brak presji na monetyzację danych użytkowników
  • Transparentny model biznesowy (płacisz za usługę, nie jesteś produktem)
  • Długoterminowa stabilność bez ryzyka przejęcia przez korporację

Plausible vs Google Analytics

CechaPlausibleGoogle Analytics 4
CookiesNieTak (first-party)
GDPR bannerNie wymagaWymaga
Rozmiar skryptu<1KB45KB+
Dane osoboweNie zbieraZbiera
Profiling użytkownikówNieTak
Udostępnianie GoogleNieTak (domyślnie)
Open sourceTak (AGPL)Nie
Self-hostingTakNie
CenaOd $9/mo"Darmowy"*
KompleksowośćProstyBardzo złożony
Retencja danychNieokreślona2-14 miesięcy
Real-timeTakOgraniczony
Lokalizacja danychEU (Niemcy)USA (Google Cloud)

*Google Analytics jest "darmowy", ale płacisz danymi swoich użytkowników, które Google wykorzystuje do reklam.

Plausible vs inne alternatywy

CechaPlausibleFathomSimple AnalyticsMatomo
Open sourceTakNieNieTak
Self-hostTakNieNieTak
Cena (start)$9/mo$14/mo$9/moFree
CookiesNieNieNieOpcjonalne
Rozmiar JS<1KB2KB3KB22KB+
EU hostingTakTakTakOpcjonalne
Real-timeTakTakTakTak
Custom eventsTakTakTakTak
FunnelsTakTakTakTak
APITakTakTakTak

Instalacja Plausible

Podstawowa instalacja (HTML)

Dodaj skrypt do sekcji <head> swojej strony:

Code
HTML
<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="UTF-8">
    <title>Moja strona</title>

    <!-- Plausible Analytics -->
    <script
        defer
        data-domain="twojadomena.pl"
        src="https://plausible.io/js/script.js">
    </script>
</head>
<body>
    <!-- Zawartość strony -->
</body>
</html>

Next.js App Router

TSapp/layout.tsx
TypeScript
// app/layout.tsx
import Script from 'next/script'

export default function RootLayout({
  children
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="pl">
      <head>
        <Script
          defer
          data-domain="twojadomena.pl"
          src="https://plausible.io/js/script.js"
          strategy="afterInteractive"
        />
      </head>
      <body>{children}</body>
    </html>
  )
}

Next.js Pages Router

TSpages/_app.tsx
TypeScript
// pages/_app.tsx
import Script from 'next/script'
import type { AppProps } from 'next/app'

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Script
        defer
        data-domain="twojadomena.pl"
        src="https://plausible.io/js/script.js"
        strategy="afterInteractive"
      />
      <Component {...pageProps} />
    </>
  )
}

Remix

TSapp/root.tsx
TypeScript
// app/root.tsx
import { Links, Meta, Outlet, Scripts } from '@remix-run/react'

export default function App() {
  return (
    <html lang="pl">
      <head>
        <Meta />
        <Links />
        <script
          defer
          data-domain="twojadomena.pl"
          src="https://plausible.io/js/script.js"
        />
      </head>
      <body>
        <Outlet />
        <Scripts />
      </body>
    </html>
  )
}

Astro

Code
ASTRO
---
// src/layouts/Layout.astro
---
<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
    <title>{title}</title>
    <script
        defer
        data-domain="twojadomena.pl"
        src="https://plausible.io/js/script.js">
    </script>
</head>
<body>
    <slot />
</body>
</html>

Gatsby

JSgatsby-ssr.js
JavaScript
// gatsby-ssr.js
import React from 'react'

export const onRenderBody = ({ setHeadComponents }) => {
  setHeadComponents([
    <script
      key="plausible"
      defer
      data-domain="twojadomena.pl"
      src="https://plausible.io/js/script.js"
    />
  ])
}

Nuxt.js 3

TSnuxt.config.ts
TypeScript
// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [
        {
          defer: true,
          'data-domain': 'twojadomena.pl',
          src: 'https://plausible.io/js/script.js'
        }
      ]
    }
  }
})

SvelteKit

src/app.html
HTML
<!-- src/app.html -->
<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    %sveltekit.head%
    <script
        defer
        data-domain="twojadomena.pl"
        src="https://plausible.io/js/script.js">
    </script>
</head>
<body data-sveltekit-preload-data="hover">
    <div style="display: contents">%sveltekit.body%</div>
</body>
</html>

Custom Events

Śledzenie zdarzeń z JavaScript

Code
TypeScript
// Deklaracja typów dla TypeScript
declare global {
  interface Window {
    plausible: (
      event: string,
      options?: {
        props?: Record<string, string | number | boolean>
        callback?: () => void
        revenue?: { currency: string; amount: number }
      }
    ) => void
  }
}

// Podstawowe zdarzenie
function trackSignup() {
  window.plausible('Signup')
}

// Zdarzenie z właściwościami
function trackPurchase(plan: string, price: number) {
  window.plausible('Purchase', {
    props: {
      plan,
      price: price.toString()
    }
  })
}

// Zdarzenie z revenue tracking
function trackSale(amount: number, currency: string = 'PLN') {
  window.plausible('Sale', {
    revenue: { currency, amount }
  })
}

// Zdarzenie z callback
function trackDownload(fileName: string) {
  window.plausible('Download', {
    props: { file: fileName },
    callback: () => {
      console.log('Event tracked successfully')
    }
  })
}

Hook do React

TShooks/usePlausible.ts
TypeScript
// hooks/usePlausible.ts
import { useCallback } from 'react'

type PlausibleEvent = {
  name: string
  props?: Record<string, string | number | boolean>
  revenue?: { currency: string; amount: number }
}

export function usePlausible() {
  const trackEvent = useCallback(({ name, props, revenue }: PlausibleEvent) => {
    if (typeof window !== 'undefined' && window.plausible) {
      window.plausible(name, { props, revenue })
    }
  }, [])

  const trackPageview = useCallback((url?: string) => {
    if (typeof window !== 'undefined' && window.plausible) {
      window.plausible('pageview', {
        props: url ? { url } : undefined
      })
    }
  }, [])

  return { trackEvent, trackPageview }
}

// Użycie w komponencie
function PricingCard({ plan, price }: { plan: string; price: number }) {
  const { trackEvent } = usePlausible()

  const handlePurchase = () => {
    trackEvent({
      name: 'Purchase',
      props: { plan },
      revenue: { currency: 'PLN', amount: price }
    })
  }

  return (
    <button onClick={handlePurchase}>
      Kup plan {plan} za {price} PLN
    </button>
  )
}

Automatyczne śledzenie kliknięć

Code
HTML
<!-- Dodaj klasę plausible-event-name=NAZWA do elementu -->
<a
  href="/pricing"
  class="plausible-event-name=CTA+Click"
>
  Zobacz cennik
</a>

<!-- Z właściwościami -->
<button
  class="plausible-event-name=Button+Click plausible-event-variant=primary"
>
  Zapisz się
</button>

<!-- Dla formularzy -->
<form
  action="/submit"
  class="plausible-event-name=Form+Submit"
>
  <input type="email" name="email" required />
  <button type="submit">Wyślij</button>
</form>

Rozszerzone opcje skryptu

Code
HTML
<!-- Automatyczne śledzenie outbound links -->
<script
  defer
  data-domain="twojadomena.pl"
  src="https://plausible.io/js/script.outbound-links.js">
</script>

<!-- Śledzenie pobrań plików -->
<script
  defer
  data-domain="twojadomena.pl"
  src="https://plausible.io/js/script.file-downloads.js">
</script>

<!-- Wszystkie rozszerzenia -->
<script
  defer
  data-domain="twojadomena.pl"
  src="https://plausible.io/js/script.tagged-events.outbound-links.file-downloads.js">
</script>

<!-- Hash-based routing (SPA) -->
<script
  defer
  data-domain="twojadomena.pl"
  src="https://plausible.io/js/script.hash.js">
</script>

<!-- Manual pageview tracking dla SPA -->
<script
  defer
  data-domain="twojadomena.pl"
  src="https://plausible.io/js/script.manual.js">
</script>

Goals & Conversions

Definiowanie celów w dashboardzie

W panelu Plausible możesz zdefiniować różne typy celów:

1. Odwiedziny strony (Pageview goals)

  • /pricing - odwiedziny strony cennika
  • /thank-you - strona podziękowania po zakupie
  • /signup/complete - zakończenie rejestracji

2. Custom events

  • Signup - rejestracja użytkownika
  • Purchase - zakup
  • Download - pobranie pliku
  • Newsletter - zapis do newslettera

3. Funnels (lejki konwersji)

Code
TEXT
Funnel: Proces zakupowy
1. /products →
2. /cart →
3. /checkout →
4. /thank-you

Konwersja: 2.3%

Revenue tracking

Code
TypeScript
// Śledzenie przychodu
function trackPurchase(orderId: string, amount: number) {
  window.plausible('Purchase', {
    revenue: {
      currency: 'PLN',
      amount
    },
    props: {
      order_id: orderId
    }
  })
}

// Przykład: e-commerce checkout
async function completeCheckout(cart: CartItem[]) {
  const total = cart.reduce((sum, item) => sum + item.price * item.quantity, 0)

  const response = await fetch('/api/checkout', {
    method: 'POST',
    body: JSON.stringify(cart)
  })

  if (response.ok) {
    const { orderId } = await response.json()

    // Track w Plausible
    window.plausible('Purchase', {
      revenue: { currency: 'PLN', amount: total },
      props: {
        order_id: orderId,
        items_count: cart.length.toString()
      }
    })
  }
}

Filtrowanie i segmentacja

Dostępne filtry w dashboardzie

Plausible pozwala filtrować dane według:

Źródła ruchu:

  • Source (Google, Facebook, Twitter, itp.)
  • Referrer (konkretna strona odsyłająca)
  • UTM parameters (campaign, source, medium, term, content)

Technologia:

  • Browser (Chrome, Firefox, Safari, Edge)
  • OS (Windows, macOS, iOS, Android, Linux)
  • Device type (Desktop, Mobile, Tablet)
  • Country / Region / City

Strony:

  • Entry page (strona wejścia)
  • Exit page (strona wyjścia)
  • Page (odwiedzona strona)

UTM tracking

Code
HTML
<!-- Link z UTM parameters -->
<a href="https://twojadomena.pl?utm_source=newsletter&utm_medium=email&utm_campaign=summer_sale">
  Sprawdź promocję
</a>
Code
TypeScript
// Generowanie linków z UTM w JavaScript
function createUtmLink(
  baseUrl: string,
  params: {
    source: string
    medium: string
    campaign: string
    term?: string
    content?: string
  }
) {
  const url = new URL(baseUrl)
  url.searchParams.set('utm_source', params.source)
  url.searchParams.set('utm_medium', params.medium)
  url.searchParams.set('utm_campaign', params.campaign)
  if (params.term) url.searchParams.set('utm_term', params.term)
  if (params.content) url.searchParams.set('utm_content', params.content)
  return url.toString()
}

// Użycie
const newsletterLink = createUtmLink('https://mojsklep.pl/promocja', {
  source: 'newsletter',
  medium: 'email',
  campaign: 'black_friday_2024'
})

Plausible API

Stats API

Code
TypeScript
// Pobieranie statystyk z Plausible API
const PLAUSIBLE_API_KEY = process.env.PLAUSIBLE_API_KEY
const SITE_ID = 'twojadomena.pl'

interface PlausibleStats {
  results: {
    visitors: { value: number }
    pageviews: { value: number }
    bounce_rate: { value: number }
    visit_duration: { value: number }
  }
}

async function getStats(period: string = '30d'): Promise<PlausibleStats> {
  const response = await fetch(
    `https://plausible.io/api/v1/stats/aggregate?` +
    `site_id=${SITE_ID}&period=${period}&` +
    `metrics=visitors,pageviews,bounce_rate,visit_duration`,
    {
      headers: {
        Authorization: `Bearer ${PLAUSIBLE_API_KEY}`
      }
    }
  )

  return response.json()
}

// Pobieranie top stron
interface TopPages {
  results: Array<{
    page: string
    visitors: number
  }>
}

async function getTopPages(limit: number = 10): Promise<TopPages> {
  const response = await fetch(
    `https://plausible.io/api/v1/stats/breakdown?` +
    `site_id=${SITE_ID}&period=30d&property=event:page&limit=${limit}`,
    {
      headers: {
        Authorization: `Bearer ${PLAUSIBLE_API_KEY}`
      }
    }
  )

  return response.json()
}

Real-time API

Code
TypeScript
// Pobieranie aktywnych użytkowników
async function getCurrentVisitors(): Promise<number> {
  const response = await fetch(
    `https://plausible.io/api/v1/stats/realtime/visitors?site_id=${SITE_ID}`,
    {
      headers: {
        Authorization: `Bearer ${PLAUSIBLE_API_KEY}`
      }
    }
  )

  return response.json()
}

// Komponent pokazujący aktywnych użytkowników
function LiveVisitors() {
  const [visitors, setVisitors] = useState<number>(0)

  useEffect(() => {
    const fetchVisitors = async () => {
      const count = await getCurrentVisitors()
      setVisitors(count)
    }

    fetchVisitors()
    const interval = setInterval(fetchVisitors, 30000) // co 30 sekund

    return () => clearInterval(interval)
  }, [])

  return (
    <div className="flex items-center gap-2">
      <span className="relative flex h-3 w-3">
        <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" />
        <span className="relative inline-flex rounded-full h-3 w-3 bg-green-500" />
      </span>
      <span>{visitors} aktywnych użytkowników</span>
    </div>
  )
}

Events API

Code
TypeScript
// Wysyłanie zdarzeń bezpośrednio przez API (server-side)
async function trackServerEvent(
  eventName: string,
  props?: Record<string, string>,
  userAgent?: string,
  ip?: string
) {
  await fetch('https://plausible.io/api/event', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'User-Agent': userAgent || 'Node.js',
      'X-Forwarded-For': ip || ''
    },
    body: JSON.stringify({
      name: eventName,
      url: `https://${SITE_ID}`,
      domain: SITE_ID,
      props
    })
  })
}

// Użycie w API route (Next.js)
export async function POST(request: Request) {
  const { action, data } = await request.json()

  // Logika biznesowa...

  // Track w Plausible
  await trackServerEvent(
    'API Action',
    { action, status: 'success' },
    request.headers.get('user-agent') || undefined,
    request.headers.get('x-forwarded-for') || undefined
  )

  return Response.json({ success: true })
}

Self-hosting Plausible

Docker Compose setup

docker-compose.yml
YAML
# docker-compose.yml
version: "3.8"

services:
  plausible:
    image: plausible/analytics:v2.0
    restart: always
    command: sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"
    depends_on:
      - plausible_db
      - plausible_events_db
    ports:
      - 8000:8000
    env_file:
      - plausible-conf.env

  plausible_db:
    image: postgres:14-alpine
    restart: always
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=postgres

  plausible_events_db:
    image: clickhouse/clickhouse-server:23.3.7.5-alpine
    restart: always
    volumes:
      - event-data:/var/lib/clickhouse
      - ./clickhouse/clickhouse-config.xml:/etc/clickhouse-server/config.d/logging.xml:ro
      - ./clickhouse/clickhouse-user-config.xml:/etc/clickhouse-server/users.d/logging.xml:ro
    ulimits:
      nofile:
        soft: 262144
        hard: 262144

volumes:
  db-data:
  event-data:

Konfiguracja environment

plausible-conf.env
ENV
# plausible-conf.env

# Required settings
BASE_URL=https://analytics.twojadomena.pl
SECRET_KEY_BASE=losowy-64-znakowy-string-wygeneruj-przez-openssl

# Database
DATABASE_URL=postgres://postgres:postgres@plausible_db:5432/plausible_db
CLICKHOUSE_DATABASE_URL=http://plausible_events_db:8123/plausible_events_db

# Email (opcjonalnie, dla powiadomień)
MAILER_EMAIL=plausible@twojadomena.pl
SMTP_HOST_ADDR=smtp.twojadomena.pl
SMTP_HOST_PORT=587
SMTP_USER_NAME=plausible@twojadomena.pl
SMTP_USER_PWD=haslo-smtp
SMTP_HOST_SSL_ENABLED=false
SMTP_RETRIES=2

# Opcjonalne
DISABLE_REGISTRATION=true
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=

Uruchomienie

Code
Bash
# Pobierz pliki konfiguracyjne
git clone https://github.com/plausible/community-edition.git plausible
cd plausible

# Wygeneruj secret key
openssl rand -base64 64 | tr -d '\n'

# Edytuj plausible-conf.env z wygenerowanym kluczem

# Uruchom
docker compose up -d

# Sprawdź logi
docker compose logs -f plausible

# Plausible będzie dostępny na http://localhost:8000

Nginx reverse proxy

Code
NGINX
# /etc/nginx/sites-available/plausible
server {
    listen 80;
    server_name analytics.twojadomena.pl;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name analytics.twojadomena.pl;

    ssl_certificate /etc/letsencrypt/live/analytics.twojadomena.pl/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/analytics.twojadomena.pl/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Cache dla skryptu JS
    location = /js/script.js {
        proxy_pass http://127.0.0.1:8000/js/script.js;
        proxy_set_header Host $host;
        proxy_cache_valid 200 1h;
        add_header Cache-Control "public, max-age=3600";
    }
}

Proxy skryptu przez własny serwer

Aby uniknąć blokowania przez ad-blockery:

TSapp/api/script.js/route.ts
TypeScript
// app/api/script.js/route.ts (Next.js)
export async function GET() {
  const response = await fetch('https://plausible.io/js/script.js')
  const script = await response.text()

  // Zamień endpoint na własny
  const modifiedScript = script.replace(
    'https://plausible.io/api/event',
    'https://twojadomena.pl/api/event'
  )

  return new Response(modifiedScript, {
    headers: {
      'Content-Type': 'application/javascript',
      'Cache-Control': 'public, max-age=3600'
    }
  })
}

// app/api/event/route.ts
export async function POST(request: Request) {
  const body = await request.text()

  await fetch('https://plausible.io/api/event', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'User-Agent': request.headers.get('user-agent') || '',
      'X-Forwarded-For': request.headers.get('x-forwarded-for') || ''
    },
    body
  })

  return new Response('ok')
}

Integracja z frameworkami

next-plausible

Code
Bash
npm install next-plausible
TSapp/layout.tsx
TypeScript
// app/layout.tsx
import PlausibleProvider from 'next-plausible'

export default function RootLayout({
  children
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="pl">
      <head>
        <PlausibleProvider
          domain="twojadomena.pl"
          trackOutboundLinks
          trackFileDownloads
          taggedEvents
        />
      </head>
      <body>{children}</body>
    </html>
  )
}

// Użycie hooka
'use client'

import { usePlausible } from 'next-plausible'

function MyComponent() {
  const plausible = usePlausible()

  const handleClick = () => {
    plausible('Button Click', {
      props: { location: 'header', variant: 'primary' }
    })
  }

  return <button onClick={handleClick}>Kliknij</button>
}

@plausible/tracker

Code
Bash
npm install @plausible/tracker
TSlib/plausible.ts
TypeScript
// lib/plausible.ts
import Plausible from '@plausible/tracker'

export const plausible = Plausible({
  domain: 'twojadomena.pl',
  trackLocalhost: true, // dla developmentu
  apiHost: 'https://plausible.io' // lub self-hosted URL
})

// Użycie
import { plausible } from '@/lib/plausible'

plausible.trackEvent('Signup', {
  props: { plan: 'pro' }
})

// Z auto-tracking
plausible.enableAutoPageviews()
plausible.enableAutoOutboundTracking()

Vue.js plugin

TSplugins/plausible.ts
TypeScript
// plugins/plausible.ts
import { createPlausible } from 'vue-plausible'

export default defineNuxtPlugin((nuxtApp) => {
  const plausible = createPlausible({
    domain: 'twojadomena.pl',
    trackLocalhost: false
  })

  nuxtApp.vueApp.use(plausible)
})

// Użycie w komponencie
<script setup>
import { usePlausible } from 'vue-plausible'

const { trackEvent } = usePlausible()

function handleClick() {
  trackEvent('Button Click')
}
</script>

Cennik Plausible

Plausible Cloud

Pageviews / miesiącCena miesięcznieCena rocznie (zniżka)
10,000$9$90 (17% off)
100,000$19$190 (17% off)
200,000$29$290 (17% off)
500,000$49$490 (17% off)
1,000,000$69$690 (17% off)
2,000,000$89$890 (17% off)
5,000,000$129$1,290 (17% off)
10,000,000+CustomCustom

Co zawiera każdy plan:

  • Nieograniczona liczba stron
  • Nieograniczona liczba użytkowników zespołu
  • Wszystkie funkcje (goals, funnels, custom events)
  • Email reports
  • API access
  • Data export
  • EU data hosting

Self-hosted

OpcjaKoszt
Community EditionDarmowy (AGPL)
Enterprise featuresLicencja komercyjna

Wymagania self-hosted:

  • 2GB RAM minimum
  • 20GB dysku (zależy od ruchu)
  • Docker i Docker Compose
  • Domena z SSL (Let's Encrypt)

FAQ - Najczęściej zadawane pytania

Czy Plausible naprawdę nie używa cookies?

Tak, Plausible nie używa żadnych cookies ani localStorage. Identyfikacja unikalnych wizyt opiera się na hashowaniu: IP + User Agent + domena + data. Hash jest tworzony na nowo każdego dnia, więc nie ma możliwości śledzenia użytkownika między dniami.

Czy muszę wyświetlać banner GDPR?

Nie, jeśli używasz tylko Plausible. Ponieważ Plausible nie zbiera danych osobowych i nie używa cookies, nie wymaga zgody użytkownika zgodnie z GDPR. Jednak jeśli używasz innych narzędzi wymagających zgody, banner i tak będzie potrzebny.

Jak Plausible radzi sobie z ad-blockerami?

Domyślny skrypt Plausible jest blokowany przez niektóre ad-blockery. Rozwiązania:

  1. Proxy skryptu przez własny serwer
  2. Użycie własnej subdomeny (analytics.twojadomena.pl)
  3. Self-hosting całej instancji

Czy mogę używać Plausible na wielu domenach?

Tak, każda domena to osobna "strona" w dashboardzie. Możesz też agregować dane z wielu subdomen na jednym dashboardzie.

Jak długo Plausible przechowuje dane?

Plausible Cloud przechowuje dane bezterminowo - nie ma limitu retencji. W wersji self-hosted możesz skonfigurować własne zasady retencji.

Czy Plausible śledzi subdomeny?

Możesz skonfigurować Plausible do śledzenia:

  • Tylko głównej domeny
  • Wszystkich subdomen razem
  • Każdej subdomeny osobno
Code
HTML
<!-- Wszystkie subdomeny razem -->
<script data-domain="twojadomena.pl" src="..."></script>

<!-- Konkretna subdomena -->
<script data-domain="blog.twojadomena.pl" src="..."></script>

Jak zmigrować z Google Analytics?

  1. Dodaj skrypt Plausible obok GA
  2. Porównuj dane przez 2-4 tygodnie
  3. Zdefiniuj cele odpowiadające eventom z GA
  4. Usuń skrypt GA po weryfikacji

Plausible oferuje też import danych historycznych z Google Analytics.

Czy mogę używać Plausible do śledzenia aplikacji mobilnych?

Plausible jest przeznaczony głównie do stron web. Dla aplikacji mobilnych rozważ dedykowane rozwiązania jak Mixpanel, Amplitude lub PostHog. Jednak możesz używać Plausible API do wysyłania zdarzeń z aplikacji.


Plausible - privacy-first analytics

What is Plausible?

Plausible Analytics is a lightweight, open-source analytics platform created as an alternative to Google Analytics with user privacy in mind. Founded in 2019 in Estonia by Uku Taht and Marko Saric, Plausible grew out of the need for an analytics tool that does not require cookies, GDPR consent banners, and does not collect personal data.

Unlike traditional analytics tools that track users across the web using third-party cookies, Plausible uses a unique approach based on hashing IP addresses combined with User Agent. This means every visit is anonymous, and Plausible does not build user profiles or sell data to advertisers.

History and philosophy of Plausible

Plausible was created as a response to growing concerns about internet privacy and increasingly complex regulations like GDPR (Europe), CCPA (California), and LGPD (Brazil). The creators noticed that most website owners only need basic metrics -- how many people visit the site, where they come from, and which pages are popular.

Google Analytics offers thousands of metrics and dimensions, but most of them are not needed by regular content creators or small businesses. Yet it requires accepting a complicated privacy policy, installing cookie banners, and potentially exposes you to GDPR fines of up to 4% of annual revenue.

Plausible follows the "less is more" philosophy -- a simple dashboard with the most important information, without unnecessary noise and without compromising on privacy.

Why Plausible?

1. No cookies = no GDPR banners

Plausible does not use cookies or any form of persistent storage. You do not need to display annoying banners asking for cookie consent. Your site loads faster and looks more professional without those pop-ups.

2. Extremely lightweight script

The Plausible script weighs less than 1KB (gzipped), while Google Analytics is over 45KB. This means:

  • Faster page loading
  • Better Core Web Vitals score
  • Lower bandwidth usage for visitors
  • Smaller carbon footprint for your site

3. Full regulatory compliance

Plausible is fully compliant with:

  • GDPR (Europe)
  • CCPA (California)
  • PECR (UK)
  • LGPD (Brazil)
  • PIPEDA (Canada)

Data is stored on servers in the European Union, which is a requirement for many European companies and institutions.

4. Open source and self-hosting

Plausible is fully open source (AGPL license). You can:

  • See exactly how the code works
  • Run your own instance on your servers
  • Have 100% control over data
  • Never worry about vendor lock-in

5. Simple, readable dashboard

Instead of hundreds of reports and dimensions, you get one elegant dashboard with the most important information:

  • Unique visitors
  • Pageviews
  • Bounce rate
  • Time on page
  • Traffic sources
  • Popular pages
  • Geographic locations
  • Devices and browsers

6. Ethical approach to business

Plausible is a bootstrapped company without external investors. This means:

  • No pressure to monetize user data
  • Transparent business model (you pay for the service, you are not the product)
  • Long-term stability without the risk of corporate acquisition

Plausible vs Google Analytics

FeaturePlausibleGoogle Analytics 4
CookiesNoYes (first-party)
GDPR bannerNot requiredRequired
Script size<1KB45KB+
Personal dataDoes not collectCollects
User profilingNoYes
Sharing with GoogleNoYes (by default)
Open sourceYes (AGPL)No
Self-hostingYesNo
PriceFrom $9/mo"Free"*
ComplexitySimpleVery complex
Data retentionIndefinite2-14 months
Real-timeYesLimited
Data locationEU (Germany)USA (Google Cloud)

*Google Analytics is "free", but you pay with your users' data, which Google uses for advertising.

Plausible vs other alternatives

FeaturePlausibleFathomSimple AnalyticsMatomo
Open sourceYesNoNoYes
Self-hostYesNoNoYes
Price (start)$9/mo$14/mo$9/moFree
CookiesNoNoNoOptional
JS size<1KB2KB3KB22KB+
EU hostingYesYesYesOptional
Real-timeYesYesYesYes
Custom eventsYesYesYesYes
FunnelsYesYesYesYes
APIYesYesYesYes

Installing Plausible

Basic installation (HTML)

Add the script to the <head> section of your page:

Code
HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>My website</title>

    <!-- Plausible Analytics -->
    <script
        defer
        data-domain="yourdomain.com"
        src="https://plausible.io/js/script.js">
    </script>
</head>
<body>
    <!-- Page content -->
</body>
</html>

Next.js App Router

TSapp/layout.tsx
TypeScript
// app/layout.tsx
import Script from 'next/script'

export default function RootLayout({
  children
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <head>
        <Script
          defer
          data-domain="yourdomain.com"
          src="https://plausible.io/js/script.js"
          strategy="afterInteractive"
        />
      </head>
      <body>{children}</body>
    </html>
  )
}

Next.js Pages Router

TSpages/_app.tsx
TypeScript
// pages/_app.tsx
import Script from 'next/script'
import type { AppProps } from 'next/app'

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Script
        defer
        data-domain="yourdomain.com"
        src="https://plausible.io/js/script.js"
        strategy="afterInteractive"
      />
      <Component {...pageProps} />
    </>
  )
}

Remix

TSapp/root.tsx
TypeScript
// app/root.tsx
import { Links, Meta, Outlet, Scripts } from '@remix-run/react'

export default function App() {
  return (
    <html lang="en">
      <head>
        <Meta />
        <Links />
        <script
          defer
          data-domain="yourdomain.com"
          src="https://plausible.io/js/script.js"
        />
      </head>
      <body>
        <Outlet />
        <Scripts />
      </body>
    </html>
  )
}

Astro

Code
ASTRO
---
// src/layouts/Layout.astro
---
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
    <title>{title}</title>
    <script
        defer
        data-domain="yourdomain.com"
        src="https://plausible.io/js/script.js">
    </script>
</head>
<body>
    <slot />
</body>
</html>

Gatsby

JSgatsby-ssr.js
JavaScript
// gatsby-ssr.js
import React from 'react'

export const onRenderBody = ({ setHeadComponents }) => {
  setHeadComponents([
    <script
      key="plausible"
      defer
      data-domain="yourdomain.com"
      src="https://plausible.io/js/script.js"
    />
  ])
}

Nuxt.js 3

TSnuxt.config.ts
TypeScript
// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [
        {
          defer: true,
          'data-domain': 'yourdomain.com',
          src: 'https://plausible.io/js/script.js'
        }
      ]
    }
  }
})

SvelteKit

src/app.html
HTML
<!-- src/app.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    %sveltekit.head%
    <script
        defer
        data-domain="yourdomain.com"
        src="https://plausible.io/js/script.js">
    </script>
</head>
<body data-sveltekit-preload-data="hover">
    <div style="display: contents">%sveltekit.body%</div>
</body>
</html>

Custom events

Tracking events with JavaScript

Code
TypeScript
declare global {
  interface Window {
    plausible: (
      event: string,
      options?: {
        props?: Record<string, string | number | boolean>
        callback?: () => void
        revenue?: { currency: string; amount: number }
      }
    ) => void
  }
}

function trackSignup() {
  window.plausible('Signup')
}

function trackPurchase(plan: string, price: number) {
  window.plausible('Purchase', {
    props: {
      plan,
      price: price.toString()
    }
  })
}

function trackSale(amount: number, currency: string = 'USD') {
  window.plausible('Sale', {
    revenue: { currency, amount }
  })
}

function trackDownload(fileName: string) {
  window.plausible('Download', {
    props: { file: fileName },
    callback: () => {
      console.log('Event tracked successfully')
    }
  })
}

React hook

TShooks/usePlausible.ts
TypeScript
// hooks/usePlausible.ts
import { useCallback } from 'react'

type PlausibleEvent = {
  name: string
  props?: Record<string, string | number | boolean>
  revenue?: { currency: string; amount: number }
}

export function usePlausible() {
  const trackEvent = useCallback(({ name, props, revenue }: PlausibleEvent) => {
    if (typeof window !== 'undefined' && window.plausible) {
      window.plausible(name, { props, revenue })
    }
  }, [])

  const trackPageview = useCallback((url?: string) => {
    if (typeof window !== 'undefined' && window.plausible) {
      window.plausible('pageview', {
        props: url ? { url } : undefined
      })
    }
  }, [])

  return { trackEvent, trackPageview }
}

function PricingCard({ plan, price }: { plan: string; price: number }) {
  const { trackEvent } = usePlausible()

  const handlePurchase = () => {
    trackEvent({
      name: 'Purchase',
      props: { plan },
      revenue: { currency: 'USD', amount: price }
    })
  }

  return (
    <button onClick={handlePurchase}>
      Buy {plan} plan for ${price}
    </button>
  )
}

Automatic click tracking

Code
HTML
<!-- Add the class plausible-event-name=NAME to the element -->
<a
  href="/pricing"
  class="plausible-event-name=CTA+Click"
>
  View pricing
</a>

<!-- With properties -->
<button
  class="plausible-event-name=Button+Click plausible-event-variant=primary"
>
  Sign up
</button>

<!-- For forms -->
<form
  action="/submit"
  class="plausible-event-name=Form+Submit"
>
  <input type="email" name="email" required />
  <button type="submit">Submit</button>
</form>

Extended script options

Code
HTML
<!-- Automatic outbound link tracking -->
<script
  defer
  data-domain="yourdomain.com"
  src="https://plausible.io/js/script.outbound-links.js">
</script>

<!-- File download tracking -->
<script
  defer
  data-domain="yourdomain.com"
  src="https://plausible.io/js/script.file-downloads.js">
</script>

<!-- All extensions -->
<script
  defer
  data-domain="yourdomain.com"
  src="https://plausible.io/js/script.tagged-events.outbound-links.file-downloads.js">
</script>

<!-- Hash-based routing (SPA) -->
<script
  defer
  data-domain="yourdomain.com"
  src="https://plausible.io/js/script.hash.js">
</script>

<!-- Manual pageview tracking for SPA -->
<script
  defer
  data-domain="yourdomain.com"
  src="https://plausible.io/js/script.manual.js">
</script>

Goals & conversions

Defining goals in the dashboard

In the Plausible dashboard you can define different types of goals:

1. Pageview goals

  • /pricing - visits to the pricing page
  • /thank-you - thank you page after purchase
  • /signup/complete - registration completion

2. Custom events

  • Signup - user registration
  • Purchase - purchase
  • Download - file download
  • Newsletter - newsletter subscription

3. Funnels (conversion funnels)

Code
TEXT
Funnel: Purchase process
1. /products →
2. /cart →
3. /checkout →
4. /thank-you

Conversion: 2.3%

Revenue tracking

Code
TypeScript
function trackPurchase(orderId: string, amount: number) {
  window.plausible('Purchase', {
    revenue: {
      currency: 'USD',
      amount
    },
    props: {
      order_id: orderId
    }
  })
}

async function completeCheckout(cart: CartItem[]) {
  const total = cart.reduce((sum, item) => sum + item.price * item.quantity, 0)

  const response = await fetch('/api/checkout', {
    method: 'POST',
    body: JSON.stringify(cart)
  })

  if (response.ok) {
    const { orderId } = await response.json()

    window.plausible('Purchase', {
      revenue: { currency: 'USD', amount: total },
      props: {
        order_id: orderId,
        items_count: cart.length.toString()
      }
    })
  }
}

Filtering and segmentation

Available filters in the dashboard

Plausible lets you filter data by:

Traffic sources:

  • Source (Google, Facebook, Twitter, etc.)
  • Referrer (specific referring page)
  • UTM parameters (campaign, source, medium, term, content)

Technology:

  • Browser (Chrome, Firefox, Safari, Edge)
  • OS (Windows, macOS, iOS, Android, Linux)
  • Device type (Desktop, Mobile, Tablet)
  • Country / Region / City

Pages:

  • Entry page
  • Exit page
  • Page (visited page)

UTM tracking

Code
HTML
<!-- Link with UTM parameters -->
<a href="https://yourdomain.com?utm_source=newsletter&utm_medium=email&utm_campaign=summer_sale">
  Check out the promotion
</a>
Code
TypeScript
function createUtmLink(
  baseUrl: string,
  params: {
    source: string
    medium: string
    campaign: string
    term?: string
    content?: string
  }
) {
  const url = new URL(baseUrl)
  url.searchParams.set('utm_source', params.source)
  url.searchParams.set('utm_medium', params.medium)
  url.searchParams.set('utm_campaign', params.campaign)
  if (params.term) url.searchParams.set('utm_term', params.term)
  if (params.content) url.searchParams.set('utm_content', params.content)
  return url.toString()
}

const newsletterLink = createUtmLink('https://mystore.com/promo', {
  source: 'newsletter',
  medium: 'email',
  campaign: 'black_friday_2024'
})

Plausible API

Stats API

Code
TypeScript
const PLAUSIBLE_API_KEY = process.env.PLAUSIBLE_API_KEY
const SITE_ID = 'yourdomain.com'

interface PlausibleStats {
  results: {
    visitors: { value: number }
    pageviews: { value: number }
    bounce_rate: { value: number }
    visit_duration: { value: number }
  }
}

async function getStats(period: string = '30d'): Promise<PlausibleStats> {
  const response = await fetch(
    `https://plausible.io/api/v1/stats/aggregate?` +
    `site_id=${SITE_ID}&period=${period}&` +
    `metrics=visitors,pageviews,bounce_rate,visit_duration`,
    {
      headers: {
        Authorization: `Bearer ${PLAUSIBLE_API_KEY}`
      }
    }
  )

  return response.json()
}

interface TopPages {
  results: Array<{
    page: string
    visitors: number
  }>
}

async function getTopPages(limit: number = 10): Promise<TopPages> {
  const response = await fetch(
    `https://plausible.io/api/v1/stats/breakdown?` +
    `site_id=${SITE_ID}&period=30d&property=event:page&limit=${limit}`,
    {
      headers: {
        Authorization: `Bearer ${PLAUSIBLE_API_KEY}`
      }
    }
  )

  return response.json()
}

Real-time API

Code
TypeScript
async function getCurrentVisitors(): Promise<number> {
  const response = await fetch(
    `https://plausible.io/api/v1/stats/realtime/visitors?site_id=${SITE_ID}`,
    {
      headers: {
        Authorization: `Bearer ${PLAUSIBLE_API_KEY}`
      }
    }
  )

  return response.json()
}

function LiveVisitors() {
  const [visitors, setVisitors] = useState<number>(0)

  useEffect(() => {
    const fetchVisitors = async () => {
      const count = await getCurrentVisitors()
      setVisitors(count)
    }

    fetchVisitors()
    const interval = setInterval(fetchVisitors, 30000)

    return () => clearInterval(interval)
  }, [])

  return (
    <div className="flex items-center gap-2">
      <span className="relative flex h-3 w-3">
        <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" />
        <span className="relative inline-flex rounded-full h-3 w-3 bg-green-500" />
      </span>
      <span>{visitors} active users</span>
    </div>
  )
}

Events API

Code
TypeScript
async function trackServerEvent(
  eventName: string,
  props?: Record<string, string>,
  userAgent?: string,
  ip?: string
) {
  await fetch('https://plausible.io/api/event', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'User-Agent': userAgent || 'Node.js',
      'X-Forwarded-For': ip || ''
    },
    body: JSON.stringify({
      name: eventName,
      url: `https://${SITE_ID}`,
      domain: SITE_ID,
      props
    })
  })
}

export async function POST(request: Request) {
  const { action, data } = await request.json()

  await trackServerEvent(
    'API Action',
    { action, status: 'success' },
    request.headers.get('user-agent') || undefined,
    request.headers.get('x-forwarded-for') || undefined
  )

  return Response.json({ success: true })
}

Self-hosting Plausible

Docker Compose setup

docker-compose.yml
YAML
# docker-compose.yml
version: "3.8"

services:
  plausible:
    image: plausible/analytics:v2.0
    restart: always
    command: sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"
    depends_on:
      - plausible_db
      - plausible_events_db
    ports:
      - 8000:8000
    env_file:
      - plausible-conf.env

  plausible_db:
    image: postgres:14-alpine
    restart: always
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=postgres

  plausible_events_db:
    image: clickhouse/clickhouse-server:23.3.7.5-alpine
    restart: always
    volumes:
      - event-data:/var/lib/clickhouse
      - ./clickhouse/clickhouse-config.xml:/etc/clickhouse-server/config.d/logging.xml:ro
      - ./clickhouse/clickhouse-user-config.xml:/etc/clickhouse-server/users.d/logging.xml:ro
    ulimits:
      nofile:
        soft: 262144
        hard: 262144

volumes:
  db-data:
  event-data:

Environment configuration

plausible-conf.env
ENV
# plausible-conf.env

# Required settings
BASE_URL=https://analytics.yourdomain.com
SECRET_KEY_BASE=random-64-character-string-generate-with-openssl

# Database
DATABASE_URL=postgres://postgres:postgres@plausible_db:5432/plausible_db
CLICKHOUSE_DATABASE_URL=http://plausible_events_db:8123/plausible_events_db

# Email (optional, for notifications)
MAILER_EMAIL=plausible@yourdomain.com
SMTP_HOST_ADDR=smtp.yourdomain.com
SMTP_HOST_PORT=587
SMTP_USER_NAME=plausible@yourdomain.com
SMTP_USER_PWD=smtp-password
SMTP_HOST_SSL_ENABLED=false
SMTP_RETRIES=2

# Optional
DISABLE_REGISTRATION=true
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=

Running the instance

Code
Bash
# Download configuration files
git clone https://github.com/plausible/community-edition.git plausible
cd plausible

# Generate secret key
openssl rand -base64 64 | tr -d '\n'

# Edit plausible-conf.env with the generated key

# Start the services
docker compose up -d

# Check logs
docker compose logs -f plausible

# Plausible will be available at http://localhost:8000

Nginx reverse proxy

Code
NGINX
# /etc/nginx/sites-available/plausible
server {
    listen 80;
    server_name analytics.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name analytics.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/analytics.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/analytics.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location = /js/script.js {
        proxy_pass http://127.0.0.1:8000/js/script.js;
        proxy_set_header Host $host;
        proxy_cache_valid 200 1h;
        add_header Cache-Control "public, max-age=3600";
    }
}

Proxying the script through your own server

To avoid being blocked by ad blockers:

TSapp/api/script.js/route.ts
TypeScript
// app/api/script.js/route.ts (Next.js)
export async function GET() {
  const response = await fetch('https://plausible.io/js/script.js')
  const script = await response.text()

  const modifiedScript = script.replace(
    'https://plausible.io/api/event',
    'https://yourdomain.com/api/event'
  )

  return new Response(modifiedScript, {
    headers: {
      'Content-Type': 'application/javascript',
      'Cache-Control': 'public, max-age=3600'
    }
  })
}

// app/api/event/route.ts
export async function POST(request: Request) {
  const body = await request.text()

  await fetch('https://plausible.io/api/event', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'User-Agent': request.headers.get('user-agent') || '',
      'X-Forwarded-For': request.headers.get('x-forwarded-for') || ''
    },
    body
  })

  return new Response('ok')
}

Framework integrations

next-plausible

Code
Bash
npm install next-plausible
TSapp/layout.tsx
TypeScript
// app/layout.tsx
import PlausibleProvider from 'next-plausible'

export default function RootLayout({
  children
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <head>
        <PlausibleProvider
          domain="yourdomain.com"
          trackOutboundLinks
          trackFileDownloads
          taggedEvents
        />
      </head>
      <body>{children}</body>
    </html>
  )
}

'use client'

import { usePlausible } from 'next-plausible'

function MyComponent() {
  const plausible = usePlausible()

  const handleClick = () => {
    plausible('Button Click', {
      props: { location: 'header', variant: 'primary' }
    })
  }

  return <button onClick={handleClick}>Click me</button>
}

@plausible/tracker

Code
Bash
npm install @plausible/tracker
TSlib/plausible.ts
TypeScript
// lib/plausible.ts
import Plausible from '@plausible/tracker'

export const plausible = Plausible({
  domain: 'yourdomain.com',
  trackLocalhost: true,
  apiHost: 'https://plausible.io'
})

import { plausible } from '@/lib/plausible'

plausible.trackEvent('Signup', {
  props: { plan: 'pro' }
})

plausible.enableAutoPageviews()
plausible.enableAutoOutboundTracking()

Vue.js plugin

TSplugins/plausible.ts
TypeScript
// plugins/plausible.ts
import { createPlausible } from 'vue-plausible'

export default defineNuxtPlugin((nuxtApp) => {
  const plausible = createPlausible({
    domain: 'yourdomain.com',
    trackLocalhost: false
  })

  nuxtApp.vueApp.use(plausible)
})

<script setup>
import { usePlausible } from 'vue-plausible'

const { trackEvent } = usePlausible()

function handleClick() {
  trackEvent('Button Click')
}
</script>

Plausible pricing

Plausible Cloud

Pageviews / monthMonthly priceAnnual price (discount)
10,000$9$90 (17% off)
100,000$19$190 (17% off)
200,000$29$290 (17% off)
500,000$49$490 (17% off)
1,000,000$69$690 (17% off)
2,000,000$89$890 (17% off)
5,000,000$129$1,290 (17% off)
10,000,000+CustomCustom

What every plan includes:

  • Unlimited number of sites
  • Unlimited team members
  • All features (goals, funnels, custom events)
  • Email reports
  • API access
  • Data export
  • EU data hosting

Self-hosted

OptionCost
Community EditionFree (AGPL)
Enterprise featuresCommercial license

Self-hosted requirements:

  • 2GB RAM minimum
  • 20GB disk (depends on traffic)
  • Docker and Docker Compose
  • Domain with SSL (Let's Encrypt)

FAQ - frequently asked questions

Does Plausible really not use cookies?

Yes, Plausible does not use any cookies or localStorage. Unique visit identification relies on hashing: IP + User Agent + domain + date. The hash is regenerated every day, so there is no way to track a user across days.

Do I need to display a GDPR banner?

No, if you are only using Plausible. Since Plausible does not collect personal data and does not use cookies, it does not require user consent under GDPR. However, if you use other tools that require consent, you will still need the banner.

How does Plausible handle ad blockers?

The default Plausible script is blocked by some ad blockers. Solutions:

  1. Proxy the script through your own server
  2. Use a custom subdomain (analytics.yourdomain.com)
  3. Self-host the entire instance

Can I use Plausible on multiple domains?

Yes, each domain is a separate "site" in the dashboard. You can also aggregate data from multiple subdomains on a single dashboard.

How long does Plausible store data?

Plausible Cloud stores data indefinitely -- there is no retention limit. In the self-hosted version, you can configure your own retention policies.

Does Plausible track subdomains?

You can configure Plausible to track:

  • Only the main domain
  • All subdomains together
  • Each subdomain separately
Code
HTML
<!-- All subdomains together -->
<script data-domain="yourdomain.com" src="..."></script>

<!-- Specific subdomain -->
<script data-domain="blog.yourdomain.com" src="..."></script>

How to migrate from Google Analytics?

  1. Add the Plausible script alongside GA
  2. Compare data for 2-4 weeks
  3. Define goals matching your GA events
  4. Remove the GA script after verification

Plausible also offers historical data import from Google Analytics.

Can I use Plausible to track mobile apps?

Plausible is primarily designed for websites. For mobile apps, consider dedicated solutions like Mixpanel, Amplitude, or PostHog. However, you can use the Plausible API to send events from your application.