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.
Sign up at getsleek.io/sign-up and add your first site. You'll get a unique site key.
Paste this snippet before the closing </body> tag (or inside <head>):
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>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.
<!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:
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:
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:
<body>
<div id="root"></div>
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
</body>Option B — useEffect hook
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)
<!-- 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
<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)
// 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
<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.
<!-- 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.
// 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.
---
// 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
// 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)
// 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)
// 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.
- Open your Webflow project and click Project Settings (gear icon).
- Navigate to the Custom Code tab.
- Paste the script tag in the Head Code field:
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>- Click Save Changes and republish your site.
Shopify
Add Sleek to your Shopify store by editing the theme files directly.
- From your Shopify admin, go to Online Store → Themes.
- Click Actions → Edit code next to your active theme.
- In the Layout folder, open
theme.liquid. - Paste the script tag just before
</head>:
<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.
| Attribute | Required | Description |
|---|---|---|
data-site | Yes | Your unique site key from the dashboard. |
data-endpoint | No | Override the default collector URL. Useful for self-hosted deployments or proxy setups. |
Example with custom endpoint
<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
// 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
window.analytics.track(eventName, properties)| Parameter | Type | Description |
|---|---|---|
eventName | string | A human-readable name for the event (e.g. "Signup", "Add to Cart"). |
properties | object (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
<button onclick="window.analytics?.track('CTA Clicked', { button: 'hero' })">
Get Started
</button>Form submission
document.querySelector('#signup-form').addEventListener('submit', () => {
window.analytics?.track('Signup Form Submitted')
})React — event on button click
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
window.analytics?.track('Add to Cart', {
product_id: 'sku-1234',
product_name: 'Wireless Headphones',
price: 79.99,
currency: 'USD',
})Video play
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.
| Metric | Full name | Good | What it measures |
|---|---|---|---|
| LCP | Largest Contentful Paint | ≤ 2.5s | How fast the main content loads. |
| CLS | Cumulative Layout Shift | ≤ 0.1 | How much the page layout shifts unexpectedly. |
| FCP | First Contentful Paint | ≤ 1.8s | When the browser first renders any content. |
| TTFB | Time to First Byte | ≤ 800ms | How 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.
- Go to your site's Settings in the dashboard.
- Enable Public Dashboard and choose a slug (e.g.
mysite). - 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
POST https://getsleek.io/api/collect
Content-Type: application/jsonRequest body
{
"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
// 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
- 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.
- 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.
- 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.
- 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.
- Add getsleek.io to your script-src directive:
Content-Security-Policy: script-src 'self' https://getsleek.io; connect-src 'self' https://getsleek.io;- 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.