Self-hosted, GDPR-compliant real-time visitor analytics for Next.js and React applications. Track live visitors with complete privacy and no consent banners required.
- GDPR/RGPD Compliant: 100% anonymous tracking with no personal data collection
- Real-time Analytics: Live visitor count, page views, and device breakdown
- Self-hosted: Complete data ownership and control
- Invisible Tracking: Zero UI footprint, no cookies, no consent banners
- High Performance: Optimized for 10,000+ concurrent connections
- Easy Integration: 2-minute setup with React components and hooks
- Privacy First: Hashed URLs, categorized user agents, ephemeral data (5-min TTL)
Traditional analytics solutions require cookie consent banners and collect personal data. Euvia is designed from the ground up to be completely anonymous and GDPR-compliant by design, eliminating the need for consent popups while providing essential live visitor insights.
- Anonymous: Pages are base64-hashed, user agents categorized, screen sizes bucketed
- Ephemeral: All data stored in Redis with 5-minute TTL, no persistent logs
- First-party: Self-hosted WebSocket connection, no third-party services
- No PII: Zero personal identifiable information collected (no IP, no UUID, no cookies)
Watch real-time visitor analytics in action:
Beautiful, ready-to-use admin dashboard with device breakdown and top pages:
Two complete Next.js examples are included to get you started quickly:
Modern Next.js 13+ with App Router, featuring:
- Server and Client Components
- Custom dashboard with
useEuviaStatshook - Built-in admin panel
- TypeScript support
Location: examples/nextjs-app-router/
cd examples/nextjs-app-router
pnpm install
pnpm run dev # Runs on http://localhost:3000Traditional Next.js setup with Pages Router:
_app.tsxintegration- Admin dashboard page
- Compatible with Next.js 12+
- TypeScript support
Location: examples/nextjs-pages-router/
cd examples/nextjs-pages-router
pnpm install
pnpm run dev # Runs on http://localhost:3002Both examples include:
- ✅ Pre-configured Euvia tracker
- ✅ Admin dashboard pages
- ✅ Environment variable setup
- ✅ Tailwind CSS styling
- ✅ TypeScript configuration
See the examples README for detailed setup instructions.
pnpm install @euvia/liveThis project uses pnpm as the recommended package manager for several reasons:
- Disk Efficiency: pnpm uses a content-addressable store, saving up to 80% disk space compared to npm/yarn
- Speed: Faster installation times due to hard-linking instead of copying files
- Strict Dependencies: Better dependency resolution prevents phantom dependencies
- Monorepo Support: Built-in workspace support for complex projects
- Security: Improved security with isolated node_modules structure
You can still use npm or yarn, but pnpm is recommended for optimal performance and consistency with the development environment.
Install pnpm:
npm install -g pnpm
# or
curl -fsSL https://get.pnpm.io/install.sh | sh -# Clone the repository
git clone https://github.com/Teyk0o/euvia-nodejs
cd euvia-nodejs
# Start with Docker Compose
docker-compose up -d# Ensure Redis is running locally
redis-server
# Start Euvia server
npx @euvia/live serverpnpm install @euvia/live
# Create .env file
cat > .env << EOF
PORT=3001
REDIS_URL=redis://localhost:6379
STATS_TTL=300
CORS_ORIGINS=http://localhost:3000
EOF
# Start server
pnpm run server// app/layout.tsx (Next.js App Router)
import { EuviaTracker } from '@euvia/live';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<EuviaTracker serverUrl="ws://localhost:3001" />
</body>
</html>
);
}// pages/_app.tsx (Next.js Pages Router)
import { EuviaTracker } from '@euvia/live';
export default function App({ Component, pageProps }) {
return (
<>
<Component {...pageProps} />
<EuviaTracker serverUrl="ws://localhost:3001" />
</>
);
}'use client'; // Next.js 13+ App Router
import { useEuviaStats } from '@euvia/live';
export default function AdminDashboard() {
const { stats, isConnected, isLoading } = useEuviaStats({
serverUrl: 'ws://localhost:3001',
});
if (isLoading) return <div>Loading stats...</div>;
if (!stats) return <div>No data available</div>;
return (
<div>
<h1>Live Visitors: {stats.totalVisitors}</h1>
<h2>Device Breakdown</h2>
<ul>
<li>Desktop: {stats.deviceBreakdown.desktop}</li>
<li>Mobile: {stats.deviceBreakdown.mobile}</li>
<li>Tablet: {stats.deviceBreakdown.tablet}</li>
</ul>
<h2>Top Pages</h2>
<ul>
{stats.topPages.map((page) => (
<li key={page.pageHash}>
{page.originalPath}: {page.visitors} visitors
</li>
))}
</ul>
</div>
);
}import { EuviaLiveStats } from '@euvia/live';
export default function StatsPage() {
return (
<div>
<h1>Analytics Dashboard</h1>
<EuviaLiveStats serverUrl="ws://localhost:3001" maxPages={10} showPaths={true} />
</div>
);
}Invisible tracking component that sends visitor heartbeats.
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
serverUrl |
string |
required | WebSocket server URL (e.g., ws://localhost:3001) |
heartbeatInterval |
number |
60000 |
Heartbeat interval in milliseconds |
enabled |
boolean |
true |
Enable/disable tracking |
onConnect |
() => void |
- | Callback when connected |
onDisconnect |
() => void |
- | Callback when disconnected |
onError |
(error: Error) => void |
- | Callback on error |
Pre-built component to display live statistics.
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
serverUrl |
string |
required | WebSocket server URL |
autoConnect |
boolean |
true |
Auto-connect on mount |
className |
string |
'' |
Custom CSS class |
showPaths |
boolean |
true |
Show unhashed paths |
maxPages |
number |
10 |
Max pages to display |
refreshInterval |
number |
5000 |
Stats refresh interval |
React hook for live statistics.
Options:
{
serverUrl: string;
autoConnect?: boolean;
onConnect?: () => void;
onDisconnect?: () => void;
onError?: (error: Error) => void;
}Returns:
{
stats: LiveStats | null;
isConnected: boolean;
isLoading: boolean;
error: Error | null;
refresh: () => void;
connect: () => void;
disconnect: () => void;
}PORT=3001 # Server port
REDIS_URL=redis://localhost:6379 # Redis connection URL
STATS_TTL=300 # Stats TTL in seconds (default: 5 minutes)
CORS_ORIGINS=* # Comma-separated allowed origins# Start server with default settings
npx @euvia/live server
# Custom configuration
npx @euvia/live server \
--port 3001 \
--redis redis://localhost:6379 \
--ttl 300 \
--cors "http://localhost:3000,https://example.com"import { createEuviaServer } from '@euvia/live/server';
const server = createEuviaServer({
port: 3001,
redisUrl: 'redis://localhost:6379',
statsTTL: 300,
corsOrigins: ['http://localhost:3000'],
broadcastInterval: 2000,
});
await server.start();
// Graceful shutdown
process.on('SIGTERM', async () => {
await server.stop();
});interface VisitorData {
pageHash: string;
deviceCategory: 'mobile' | 'desktop' | 'tablet';
screenBucket: string;
timestamp: number;
}
interface LiveStats {
totalVisitors: number;
topPages: PageStats[];
deviceBreakdown: {
mobile: number;
desktop: number;
tablet: number;
};
lastUpdate: number;
}
interface PageStats {
pageHash: string;
originalPath?: string;
visitors: number;
}# Build and run
docker-compose up -d
# View logs
docker-compose logs -f euvia-server
# Scale if needed
docker-compose up -d --scale euvia-server=3For production, create a .env file:
NODE_ENV=production
PORT=3001
REDIS_URL=redis://your-redis-host:6379
STATS_TTL=300
CORS_ORIGINS=https://yourdomain.comupstream euvia_backend {
server localhost:3001;
}
server {
listen 443 ssl http2;
server_name analytics.yourdomain.com;
location / {
proxy_pass http://euvia_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
}
}- Client Bundle: < 10KB gzipped (tree-shakable)
- Server Capacity: 10,000+ concurrent connections
- Latency: < 50ms response time
- Memory: ~50MB base + ~1KB per active visitor
- Redis: Optimized with pipelines and TTL-based cleanup
Euvia is designed to be GDPR-compliant by default. Here's why no consent banner is required:
- No Personal Data: No IP addresses, user IDs, cookies, or fingerprinting
- Anonymized: All data is categorized and hashed before storage
- Aggregated: Only statistical aggregates are computed
Under GDPR Article 6(1)(f), legitimate interest applies when:
- Analytics are necessary for site operation
- Privacy impact is minimal (anonymous, ephemeral)
- No unexpected processing occurs
Euvia meets all criteria as a first-party, anonymous analytics tool.
Include this in your privacy policy:
We use self-hosted analytics (Euvia) to understand site usage. This collects anonymous data including:
- Page views (hashed URLs)
- Device categories (mobile/desktop/tablet)
- Screen size ranges
No personal information, IP addresses, or cookies are collected. Data is deleted after 5 minutes.
- Node.js 20+ (required)
- pnpm (recommended) - see installation above
- Redis 7+ (for local server)
- Git (for version control)
# Clone repository
git clone https://github.com/Teyk0o/euvia-nodejs
cd euvia-nodejs
# Install dependencies (auto-installs git hooks)
pnpm install
# Start development
pnpm run dev
# Run tests
pnpm test
# Run tests with coverage
pnpm run test:coverage
# Lint code
pnpm run lint
# Format code
pnpm run format
# Type check
pnpm run typecheck
# Build
pnpm run buildThis project uses automated quality checks and conventional commits.
Automatically runs before each commit:
- ESLint: Auto-fixes TypeScript/React code issues
- Prettier: Formats all code files
- Tests: Runs tests for changed files only
- Type Check: Validates TypeScript types
Runs before pushing to remote:
- Full Test Suite: Ensures all tests pass
- Type Check: Complete TypeScript validation
- Security Audit: Checks for vulnerable dependencies
All commits must follow Conventional Commits:
<type>[optional scope]: <description>
[optional body]
[optional footer]
Types:
feat- New featurefix- Bug fixdocs- Documentation changesstyle- Code formatting (no logic change)refactor- Code refactoringperf- Performance improvementstest- Adding/updating testsbuild- Build system or dependenciesci- CI/CD changeschore- Other changes
Examples:
feat(tracker): add reconnection callbacks
fix(server): resolve Redis connection leak
docs: update deployment guide
refactor(utils): simplify hash functionValidation: Commit messages are validated automatically. Invalid formats will be rejected:
✓ feat(tracker): add new feature
✓ fix: resolve bug
✗ Add new feature # Missing type
✗ FEAT: new feature # Uppercase not allowedBypass Hooks (Emergency Only):
git commit --no-verify -m "emergency fix"Install the Conventional Commits extension for easier commit message creation:
- Extension ID:
vivaxy.vscode-conventional-commits - Use
Ctrl+Shift+P→ "Conventional Commits"
commitlint.config.js- Commit message validation.husky/- Git hooks (pre-commit, pre-push, commit-msg).lintstagedrc.json- Lint-staged configuration.prettierrc.json- Code formatting rules.pre-commit-config.yaml- Python pre-commit (alternative)
# Run all tests
pnpm test
# Watch mode
pnpm run test:watch
# Coverage report
pnpm run test:coverageBefore pushing changes, it's recommended to test GitHub Actions workflows locally using act. This helps catch CI issues early and saves time by avoiding unnecessary commits.
Install act:
# macOS
brew install act
# Linux
curl -s https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
# Windows (with chocolatey)
choco install act-cliRun workflows locally:
# Test all workflows
act
# Test specific job
act -j test
# Test docker build
act -j docker
# Run with verbose output
act -vNote: act requires Docker to be running. You may need to provide GitHub secrets via .secrets file for certain workflows.
┌─────────────────┐
│ Next.js App │
│ (EuviaTracker) │
└────────┬────────┘
│ WebSocket
▼
┌─────────────────┐
│ Euvia Server │
│ Express + WS │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Redis Cache │
│ (5min TTL) │
└─────────────────┘
▲
│
┌────────┴────────┐
│ Admin Client │
│ (EuviaLiveStats)│
└─────────────────┘
- Horizontal scaling with Redis Cluster
- Historical data export (CSV, JSON)
- Custom event tracking
- Geographic regions (country-level only)
- Referrer tracking (hashed)
- Session duration estimation
- GraphQL API support
Contributions are welcome! Please read our Contributing Guide first.
# Fork and clone
git clone https://github.com/yourusername/euvia-nodejs
cd euvia-nodejs
# Create feature branch
git checkout -b feature/amazing-feature
# Make changes and test
pnpm test
# Commit and push
git commit -m "Add amazing feature"
git push origin feature/amazing-featureMIT License - see LICENSE file for details.
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: Wiki
Built with:
- Socket.io - Real-time WebSocket communication
- Redis - High-performance data storage
- Express - Web server framework
- TypeScript - Type-safe development
Made with privacy in mind by Euvia Contributors

