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.
CLI (Recommended)
The fastest way to add Sleek to any project. Our CLI auto-detects your framework and injects the tracking script into the right file.
Quick setup
npx sleek-analytics initThat's it. The CLI will:
- Detect your framework automatically
- Ask for your Site ID (from your Sleek dashboard)
- Inject the analytics script into the correct file
Supported frameworks
| Framework | How it injects |
|---|---|
| Next.js (App Router) | <Script> in app/layout |
| Next.js (Pages Router) | <Script> in pages/_app |
| React (Vite / CRA) | <script> in index.html |
| Vue.js | <script> in index.html |
| Nuxt.js | app.head.script in nuxt.config |
| SvelteKit | <svelte:head> in +layout.svelte |
| Remix | <script> in app/root |
| Astro | <script> in layout .astro file |
| Gatsby | setHeadComponents in gatsby-ssr.js |
| WordPress | wp_head hook in functions.php |
| HTML / Static | <script> in index.html |
Global install
If you prefer to install it globally:
npm i -g sleek-analytics
sleek-analytics initWhat gets added
A single async script tag pointing to your Sleek instance:
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_ID"></script>The script is ~1 KB, loads asynchronously, and doesn't slow down your site.
Requires Node.js 16 or later. The CLI only modifies a single file in your project — no config files, wrappers, or build plugins.
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.
Revenue Attribution
Connect your Stripe account to see revenue data directly inside your analytics dashboard. Understand which pages, referrers, and countries drive the most revenue.
How it works
Sleek fetches your Stripe charges using a restricted API key and displays them alongside your traffic data. Revenue appears as an overlay on your time series chart and as a dedicated card on the overview page.
Setup
- Go to your Stripe Dashboard → API keys.
- Click Create restricted key.
- Give it a name (e.g. "Sleek Analytics") and enable read-only access to Charges. No other permissions are needed.
- Copy the key (starts with
rk_live_orrk_test_). - In Sleek, go to your site's Settings and paste the key in the Stripe Revenue section.
What you'll see
- Revenue card on the overview page showing total revenue for the selected time range
- Revenue overlay on the main traffic chart
- Recent payments list with amount, status, and date
- Revenue data respects the same time range filters as the rest of your dashboard (today, 7d, 30d, etc.)
Your Stripe key is encrypted with AES-256-GCM before being stored. Sleek only has read-only access to your charges — it cannot create payments, modify subscriptions, or access any other Stripe data.
Disconnecting
To remove the Stripe connection, go to Settings and click Disconnect in the Stripe Revenue section. Your encrypted key will be deleted immediately.
Revenue attribution requires a Stripe restricted key starting with rk_. Full API keys (sk_) are not accepted for security reasons.
Plausible Import
Switching from Plausible? You can import your full historical data into Sleek in one click — zero data loss.
Exporting from Plausible
- Log in to your Plausible dashboard.
- Go to your site's Settings → Imports & Exports.
- Click Export. Plausible will generate a
.zipfile containing CSV files for visitors, pages, sources, and more. - Download the ZIP file to your computer.
Importing into Sleek
- In Sleek, go to your site's Settings → Import section.
- Click Upload and select the ZIP file you exported from Plausible.
- Sleek will parse the CSV files and import your data. This usually takes a few seconds.
- Once complete, you'll see the imported date range displayed in the settings.
What gets imported
- Visitors — daily visitor and pageview counts
- Pages — top pages with visit counts
- Sources — traffic sources and referrers
- Devices, browsers, OS — device breakdown data
- Countries — geographic breakdown
Imported data is merged with your live Sleek data in all dashboard views. When you select a time range that covers the import period, both datasets are combined seamlessly.
The import is a one-time operation. After importing, new data is collected by the Sleek tracking script. You can remove the imported data at any time from Settings.
Removing imported data
If you want to start fresh, go to Settings → Import and click Remove. This deletes only the imported Plausible data — your live Sleek data is not affected.
Only ZIP files exported from Plausible are supported. Other analytics platforms are not supported for import at this time.
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.