Sleek Analytics
introduction
Documentation

Introduction

Sleek Analytics is a lightweight, privacy-first analytics platform. Add one script tag and get real-time visitor insights without cookies, consent banners, or GDPR headaches.

Under 2 KB

Minified & gzipped. Won't slow down your site.

🔒

Cookie-free

No cookies, no personal data. GDPR / CCPA ready.

📡

Real-time

See live visitor counts and active pages instantly.

Quick Start

Get up and running in under two minutes.

1Create your account

Sign up at getsleek.io/sign-up and add your first site. You'll get a unique site key.

2Add the script to your site

Paste this snippet before the closing </body> tag (or inside <head>):

html
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
3Visit your dashboard

Open your dashboard at getsleek.io/dashboard. Data appears within seconds of the first pageview.

💡

Replace YOUR_SITE_KEY with the actual key shown in your dashboard under Settings → Site Key.

HTML & Vanilla JS

The simplest installation — drop the script anywhere in your HTML.

html
<!DOCTYPE html>
<html>
  <head>
    <title>My Site</title>
    <!-- Sleek Analytics -->
    <script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
  </head>
  <body>
    <!-- your content -->
  </body>
</html>
💡

Using async ensures the script never blocks page rendering.

Next.js

Use the built-in Next.js Script component so the analytics script loads with the correct strategy.

App Router (Next.js 13+)

Add it to your root app/layout.tsx:

tsx
import Script from 'next/script'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script
          src="https://getsleek.io/v1.js"
          data-site="YOUR_SITE_KEY"
          strategy="afterInteractive"
        />
      </body>
    </html>
  )
}

Pages Router (Next.js 12 and below)

Add it to pages/_app.tsx:

tsx
import Script from 'next/script'
import type { AppProps } from 'next/app'

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Component {...pageProps} />
      <Script
        src="https://getsleek.io/v1.js"
        data-site="YOUR_SITE_KEY"
        strategy="afterInteractive"
      />
    </>
  )
}
💡

The strategy="afterInteractive" loads the script after the page is interactive — ideal for analytics.

React (Vite / CRA)

Inject the script once inside your root component using a useEffect hook, or add it directly to public/index.html.

Option A — index.html (recommended)

Open public/index.html and add the script tag:

html
<body>
  <div id="root"></div>
  <script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
</body>

Option B — useEffect hook

tsx
import { useEffect } from 'react'

export default function App() {
  useEffect(() => {
    const s = document.createElement('script')
    s.src = 'https://getsleek.io/v1.js'
    s.setAttribute('data-site', 'YOUR_SITE_KEY')
    s.async = true
    document.head.appendChild(s)
  }, [])

  return <div>{/* your app */}</div>
}

Vue.js

Add the script to your index.html or mount it in your root App.vue.

index.html (simplest)

html
<!-- public/index.html -->
<body>
  <div id="app"></div>
  <script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
  <script type="module" src="/src/main.ts"></script>
</body>

App.vue — onMounted

vue
<script setup>
import { onMounted } from 'vue'

onMounted(() => {
  const s = document.createElement('script')
  s.src = 'https://getsleek.io/v1.js'
  s.setAttribute('data-site', 'YOUR_SITE_KEY')
  s.async = true
  document.head.appendChild(s)
})
</script>

<template>
  <RouterView />
</template>
💡

Sleek automatically hooks into history.pushState so Vue Router navigations are tracked without any extra setup.

Nuxt.js

Use Nuxt's built-in useHead composable or add the script via nuxt.config.ts.

nuxt.config.ts (recommended)

ts
// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [
        {
          src: 'https://getsleek.io/v1.js',
          'data-site': 'YOUR_SITE_KEY',
          async: true,
        },
      ],
    },
  },
})

app.vue — useHead composable

vue
<script setup>
useHead({
  script: [
    {
      src: 'https://getsleek.io/v1.js',
      'data-site': 'YOUR_SITE_KEY',
      async: true,
    },
  ],
})
</script>

<template>
  <NuxtPage />
</template>

SvelteKit

Add the script tag inside the <svelte:head> block in your root layout.

svelte
<!-- src/routes/+layout.svelte -->
<svelte:head>
  <script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
</svelte:head>

<slot />
💡

SvelteKit's router uses the History API, which Sleek automatically intercepts — no extra configuration needed for SPA tracking.

Remix

Add the script inside the <head> in your root app/root.tsx.

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

export default function App() {
  return (
    <html lang="en">
      <head>
        <Meta />
        <Links />
        <script
          async
          src="https://getsleek.io/v1.js"
          data-site="YOUR_SITE_KEY"
        />
      </head>
      <body>
        <Outlet />
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  )
}

Astro

Add the script to your base layout file, typically src/layouts/Layout.astro.

astro
---
// src/layouts/Layout.astro
---
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <slot name="head" />
    <script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
  </head>
  <body>
    <slot />
  </body>
</html>
💡

If you use Astro's viewTransitions, add a transition:persist attribute to the script tag to prevent it re-initialising on every page transition.

Gatsby

Use Gatsby's gatsby-browser.js or gatsby-ssr.js to inject the script globally.

gatsby-ssr.js

jsx
// gatsby-ssr.js
import React from 'react'

export const onRenderBody = ({ setHeadComponents }) => {
  setHeadComponents([
    <script
      key="sleek"
      async
      src="https://getsleek.io/v1.js"
      data-site="YOUR_SITE_KEY"
    />,
  ])
}

gatsby-browser.js (route change tracking)

js
// gatsby-browser.js
export const onRouteUpdate = ({ location }) => {
  // Sleek auto-detects pushState, but you can call the API manually too:
  // window.analytics?.track('gatsby:route-change', { path: location.pathname })
}

WordPress

There are two ways to add Sleek to a WordPress site.

Option A — Theme functions.php (recommended for developers)

php
// functions.php
function sleek_analytics_script() {
    echo '<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>';
}
add_action( 'wp_head', 'sleek_analytics_script' );

Option B — WordPress Admin (no coding)

Go to Appearance → Theme Editor → header.php and paste the script tag just before </head>. Alternatively, install a plugin like "Insert Headers and Footers" and paste it in the Header section.

⚠️

If you are using a page caching plugin (WP Rocket, W3 Total Cache), make sure the script tag is not excluded from the cached output.

Webflow

Webflow allows custom code in the <head> tag for all pages at once.

  1. Open your Webflow project and click Project Settings (gear icon).
  2. Navigate to the Custom Code tab.
  3. Paste the script tag in the Head Code field:
html
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
  1. Click Save Changes and republish your site.

Shopify

Add Sleek to your Shopify store by editing the theme files directly.

  1. From your Shopify admin, go to Online Store → Themes.
  2. Click Actions → Edit code next to your active theme.
  3. In the Layout folder, open theme.liquid.
  4. Paste the script tag just before </head>:
html
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
💡

This tracks across all storefront pages including product pages, collection pages, and the cart. It does not track inside Shopify admin itself.

Script options

The tracking script accepts a small set of data-* attributes to customise its behaviour.

AttributeRequiredDescription
data-siteYesYour unique site key from the dashboard.
data-endpointNoOverride the default collector URL. Useful for self-hosted deployments or proxy setups.

Example with custom endpoint

html
<script
  async
  src="https://getsleek.io/v1.js"
  data-site="YOUR_SITE_KEY"
  data-endpoint="https://analytics.yourdomain.com"
></script>

Excluding yourself

To avoid polluting your stats with your own visits, you have several options:

1. Browser extension (easiest)

Install any ad-blocker extension on your development browser (uBlock Origin, etc.). Ad-blockers typically block analytics scripts on localhost and often on your own domain.

2. Exclude via localStorage

js
// Run this in your browser console once.
// Sleek will ignore all future events from this browser.
localStorage.setItem('sleek_exclude', '1')
💡

The script checks for localStorage.sleek_exclude === '1' and silently skips sending any events.

3. Exclude by URL pattern

Events sent from localhost, 127.0.0.1, and *.local are automatically filtered out in the dashboard view.

SPA / Client-side routing

Sleek automatically tracks single-page application navigations by hooking into the browser's history.pushState and the popstate event.

This means you get automatic page tracking for:

  • React Router / TanStack Router
  • Vue Router
  • Next.js client-side navigation
  • Nuxt Router
  • SvelteKit router
  • Any router that wraps the History API
💡

Duration is tracked per-page: when the route changes, Sleek sends a duration event for the previous page before firing a new pageview.

Tracking custom events

Beyond automatic pageviews, you can send custom events to track any user interaction. Sleek exposes a global window.analytics object once the script loads.

Syntax

js
window.analytics.track(eventName, properties)
ParameterTypeDescription
eventNamestringA human-readable name for the event (e.g. "Signup", "Add to Cart").
propertiesobject (optional)Key-value pairs with additional context. Stored as JSON.
⚠️

Call window.analytics.track only after the script has loaded. If you call it immediately on page load, wrap it in a DOMContentLoaded listener or check that window.analytics exists first.

Event examples

Button click

html
<button onclick="window.analytics?.track('CTA Clicked', { button: 'hero' })">
  Get Started
</button>

Form submission

js
document.querySelector('#signup-form').addEventListener('submit', () => {
  window.analytics?.track('Signup Form Submitted')
})

React — event on button click

tsx
function UpgradeButton() {
  function handleClick() {
    window.analytics?.track('Upgrade Clicked', { plan: 'pro', source: 'banner' })
    // ... open checkout
  }

  return <button onClick={handleClick}>Upgrade to Pro</button>
}

E-commerce — add to cart

js
window.analytics?.track('Add to Cart', {
  product_id: 'sku-1234',
  product_name: 'Wireless Headphones',
  price: 79.99,
  currency: 'USD',
})

Video play

js
document.querySelector('#hero-video').addEventListener('play', () => {
  window.analytics?.track('Video Played', { video: 'hero-demo' })
})

Privacy & Compliance

Sleek is designed to be compliant by default — no consent banners required.

No cookies

Sleek does not set any cookies, first-party or third-party. There is nothing to consent to.

No personal data collected

Visitors are identified by a short-lived, anonymous fingerprint derived from browser signals (user agent, screen size, timezone). This fingerprint cannot be reversed to identify an individual.

GDPR

Because no personal data is processed and no cookies are stored, Sleek does not trigger GDPR consent requirements under most interpretations. You do not need a cookie banner for Sleek alone.

CCPA

Sleek does not sell personal information, does not build user profiles, and does not share data with third parties. CCPA does not apply.

Data storage

All analytics data is stored on servers within the EU (DigitalOcean, Amsterdam). No data is transferred to the US or other third parties.

Web Vitals

Sleek automatically collects Core Web Vitals from real users without any additional setup.

MetricFull nameGoodWhat it measures
LCPLargest Contentful Paint≤ 2.5sHow fast the main content loads.
CLSCumulative Layout Shift≤ 0.1How much the page layout shifts unexpectedly.
FCPFirst Contentful Paint≤ 1.8sWhen the browser first renders any content.
TTFBTime to First Byte≤ 800msHow quickly the server starts responding.

Vitals are collected via the PerformanceObserver API and sent as a separate vitals event after the page finishes loading. The dashboard shows the p75 value for each metric.

Public Dashboards

You can make any site's dashboard publicly viewable — no login required.

  1. Go to your site's Settings in the dashboard.
  2. Enable Public Dashboard and choose a slug (e.g. mysite).
  3. Share the URL: getsleek.io/mysite
💡

Public dashboards display the same analytics charts as your private dashboard but do not expose your site key, settings, or billing information.

Real-time visitors

The live visitor count shown at the top of your dashboard reflects visitors active in the last 5 minutes. It updates automatically every 3 seconds via a server-sent events stream.

For each active visitor, you can see:

  • The current page they are on
  • Their country (derived from IP, not stored raw)
  • Whether they arrived from a referrer
💡

The real-time feed is powered by Redis. No ClickHouse query is made for live data, so there is no latency penalty even at high traffic volumes.

Collect API

The tracking script sends events to POST /api/collect. You can also call this endpoint directly from your server or any HTTP client.

Endpoint

text
POST https://getsleek.io/api/collect
Content-Type: application/json

Request body

json
{
  "site_key":   "YOUR_SITE_KEY",        // required — your public site key
  "event_type": "pageview",             // required — pageview | custom | duration | vitals
  "session_id": "abc12345",             // required — random per-tab ID
  "visitor_id": "d4f9c2a1",             // required — cookieless fingerprint
  "url":        "https://example.com/blog/post-1",  // required
  "referrer":   "https://twitter.com",  // optional
  "duration":   42,                     // seconds (for duration events)
  "event_name": "Signup",              // custom event name
  "properties": { "plan": "pro" },     // custom event properties
  "lcp":  1200,                         // ms (vitals)
  "cls":  0.05,                         // score (vitals)
  "fcp":  800,                          // ms (vitals)
  "ttfb": 200                           // ms (vitals)
}

Response

json
// 200 OK
{ "ok": true }

// 400 Bad Request
{ "error": "Missing required fields" }

// 401 Unauthorized
{ "error": "Invalid site key" }
💡

The endpoint has Access-Control-Allow-Origin: * — it is safe to call from any browser origin.

Troubleshooting

No data is appearing in my dashboard
  • Check that the script tag is actually present in the HTML source of your page (use View Source, not DevTools Elements — DevTools may show injected nodes).
  • Confirm the data-site attribute matches the site key in your dashboard exactly.
  • Open the Network tab in DevTools and look for a POST request to /api/collect. If it is blocked, an ad-blocker may be active.
  • Data can take up to 10 seconds to appear. Hard-refresh your dashboard.
My bounce rate looks wrong / too high
  • Bounce rate is calculated per session. A session that has only one pageview counts as a bounce.
  • If your SPA navigation is not being tracked (missing pushState hook), every "page" looks like a single pageview. Confirm the script is loaded before the router initialises.
Custom events are not showing up
  • Make sure you call window.analytics.track only after the script has loaded. Wrap it in a load event listener or use optional chaining (window.analytics?.track).
  • Custom events appear under the Events tab in your dashboard, not in the main overview.
I see my own visits in the stats
  • Run localStorage.setItem("sleek_exclude", "1") in your browser console to permanently exclude yourself from that browser.
  • Alternatively, install an ad-blocker on your development machine.
The script is blocked by Content Security Policy (CSP)
  • Add getsleek.io to your script-src directive:
http
Content-Security-Policy: script-src 'self' https://getsleek.io; connect-src 'self' https://getsleek.io;
Web Vitals are missing or showing 0
  • Web Vitals rely on PerformanceObserver which is not available in all browsers (Firefox < 105 does not support LCP). Results are only included when the browser reports them.
  • Vitals are sent after the page load event — if a user navigates away immediately, the vitals event may not be sent.

Still stuck?

Email us at hello@getsleek.io. We typically respond within a few hours.