Technical Specification - Instagram Unfollow Tracker
| Version: 1.5.0 | Last Updated: January 16, 2026 |
1. Project Overview
Goal
A privacy-focused, local web application that analyzes Instagram Data Download (ZIP) files to provide insights into follower relationships without requiring Instagram authentication or sending data to external servers.
Core Features
- Unfollow tracking: Identify users you follow who donβt follow back
- Follower analysis: Find users who follow you but you donβt follow back
- Smart badges: Categorize accounts (mutuals, close friends, restricted, etc.)
- Lightning search: <2ms search with trigram/prefix indexes (1M+ accounts)
- Advanced filtering: <5ms BitSet-based filtering for any badge combination
- Direct profile links: Click to open Instagram profiles in new tabs
Privacy Principles
- 100% local processing: All data processing happens in the browser
- No Instagram login: Uses official Instagram data export only
- Open source: MIT license, full transparency and auditability
- Privacy-respecting analytics: Umami (anonymous) + Vercel (performance only)
2. Technical Architecture
Frontend Stack
| Technology | Purpose |
|ββββ|βββ|
| React 18 | UI framework with hooks and functional components |
| TypeScript | Strict mode, zero any types |
| Vite | Build tool and development server |
| vite-react-ssg | Static Site Generation (80+ pre-rendered pages) |
| shadcn/ui | Composable UI components built on Radix UI |
| Tailwind CSS | Utility-first styling with OKLCH color system |
| Zustand | Lightweight state management (<1KB UI state only) |
| i18next | Internationalization (11 languages) |
Data Storage & Processing
| Technology | Purpose | |ββββ|βββ| | IndexedDB v2 | Columnar storage with 40x compression | | FastBitSet.js | Bitwise filtering operations (75x faster) | | Comlink | Type-safe Web Worker communication | | TanStack Virtual | Virtual scrolling for 1M+ items at 60 FPS | | Web Workers | Off-thread filtering (INP: 180ms) |
Build & Deployment
| Technology | Purpose | |ββββ|βββ| | Vercel | Hosting with Edge Functions | | vite-plugin-pwa | PWA with 176 precached assets | | @fontsource | Self-hosted fonts (Inter, Plus Jakarta Sans) | | @vercel/og | Dynamic OG image generation |
Testing & Quality
| Technology | Purpose | |ββββ|βββ| | Vitest | Fast unit testing (1,601 tests) | | React Testing Library | Component testing | | @vitest/web-worker | Web Worker testing | | 98% coverage | Comprehensive test suite | | ESLint | Code quality (zero warnings) | | Husky | Git hooks for quality gates |
3. State Management
Zustand Store (<1KB constraint)
interface AppState {
// Filter state
filters: Set<BadgeKey>;
setFilters: (filters: Set<BadgeKey>) => void;
// Upload state
uploadStatus: 'idle' | 'loading' | 'success' | 'error';
uploadError: string | null;
currentFileName: string | null;
// File metadata (NOT account data)
fileMetadata: FileMetadata | null;
// Theme (3-way toggle)
theme: 'light' | 'dark' | 'system';
setTheme: (theme: 'light' | 'dark' | 'system') => void;
// Hydration
_hasHydrated: boolean;
}
Critical Constraints:
- β NO account data arrays in store
- β NO arrays >10 items
- β NO parsed data of any kind
- β NO language state (URL is source of truth)
- β If store >1KB, architecture is broken
Language Detection (URL as Source of Truth)
// src/config/languages.ts
export const SUPPORTED_LANGUAGES = ['en', 'es', 'ru', 'de', 'pt', 'tr', 'hi', 'id', 'ja', 'ar', 'fr'];
export const RTL_LANGUAGES = ['ar'];
export function detectLanguageFromUrl(): SupportedLanguage {
const pathname = window.location.pathname;
const match = pathname.match(/^\/(en|es|ru|de|pt|tr|hi|id|ja|ar|fr)(\/|$)/);
return match ? match[1] as SupportedLanguage : 'en';
}
4. IndexedDB v2 Architecture
Database: instagram-tracker-v2
| Store | Purpose | Key |
|---|---|---|
| files | File metadata registry | hash |
| columns | Username/href as packed Uint8Arrays | ${hash}:${column} |
| bitsets | Badge presence (1-bit per account) | ${hash}:${badge} |
| timestamps | Sparse time data for temporal badges | ${hash}:timestamps |
| indexes | Trigram/prefix search indexes (3-day TTL) | ${hash}:search |
Data Flow
Upload ZIP
β
Parse Worker (Web Worker)
βββ Extract ZIP (JSZip)
βββ Parse JSON files
βββ Emit 10k account chunks
β
IndexedDB Service
βββ Pack columns (Uint8Array)
βββ Update bitsets (FastBitSet)
β
Background: Build search indexes (trigram + prefix)
β
Zustand: uploadStatus = 'success'
Filter Flow (via Web Worker)
FilterChips.onClick(badge)
β
useFilterWorker.filterToIndices(query, filters)
β
Web Worker (filter-worker.ts via Comlink)
βββ Load bitsets (cached)
βββ Intersect bitsets (FastBitSet.intersection)
βββ Apply search if query
β
Result: number[] indices
β
TanStack Virtual: render visible items (~20)
β
useAccountDataSource: lazy load accounts by indices
5. Performance Specifications
Benchmarks (1M accounts)
| Metric | Target | Achieved |
|---|---|---|
| Filter (single badge) | <10ms | ~3ms |
| Filter (3 badges) | <10ms | ~5ms |
| Search (indexed) | <5ms | ~2ms |
| Storage | <20MB | ~5MB |
| Memory (runtime) | <20MB | ~5MB |
| INP | <200ms | 180ms |
| LCP | <2.5s | ~1.3s |
Optimization Strategies
- Columnar storage: 40x space reduction vs row-based
- BitSet filtering: 32x faster than boolean arrays
- Web Workers: Filter operations off main thread
- Virtual scrolling: Render only ~20 visible items
- LRU caching: 500 accounts per slice, 20 slices max
- Trigram indexes: O(1) search vs O(n) linear scan
6. Internationalization (i18n)
Supported Languages (11)
| Language | Code | RTL | Locale |
|---|---|---|---|
| English | en | β | en_US |
| EspaΓ±ol | es | β | es_ES |
| Π ΡΡΡΠΊΠΈΠΉ | ru | β | ru_RU |
| Deutsch | de | β | de_DE |
| PortuguΓͺs | pt | β | pt_BR |
| TΓΌrkΓ§e | tr | β | tr_TR |
| ΰ€Ήΰ€Ώΰ€¨ΰ₯ΰ€¦ΰ₯ | hi | β | hi_IN |
| Bahasa Indonesia | id | β | id_ID |
| ζ₯ζ¬θͺ | ja | β | ja_JP |
| Ψ§ΩΨΉΨ±Ψ¨ΩΨ© | ar | β | ar_SA |
| FranΓ§ais | fr | β | fr_FR |
SSG Architecture
- 80+ pre-rendered pages: 11 languages Γ 8 routes
- Path-based routing:
/es/wizard,/ar/upload, etc. - Localized meta tags: Dynamic title/description per language
- hreflang tags: SEO optimization for language variants
- Full page reload on language change: Ensures correct SSG meta
7. File Structure
src/
βββ core/ # Domain logic
β βββ types.ts # Core types (Account, BadgeKey, etc.)
β βββ badges/ # Badge computation logic
β βββ parsers/ # Instagram ZIP parsing
βββ lib/ # Infrastructure
β βββ store.ts # Zustand (UI state only!)
β βββ indexeddb/ # Columnar storage, bitsets
β βββ filtering/ # BitSet filter engine
β βββ search-index.ts # Trigram/prefix indexes
βββ config/ # Configuration
β βββ languages.ts # Language config (single source of truth)
βββ hooks/ # React hooks
β βββ useInstagramData.ts
β βββ useAccountFiltering.ts
β βββ useAccountDataSource.ts
β βββ useFilterWorker.ts # Web Worker hook
β βββ useLanguageFromPath.ts # Sync language from URL
β βββ useLanguagePrefix.ts # Get language prefix for nav
βββ workers/ # Web Workers
β βββ filter-worker.ts # IndexedDBFilterEngine (Comlink)
βββ pages/ # SSG page components
β βββ HomePage.tsx # / route
β βββ WizardPage.tsx # /wizard route
β βββ UploadPage.tsx # /upload route
β βββ ResultsPage.tsx # /results route
β βββ ... # 8 pages total
βββ components/ # UI components
β βββ ui/ # shadcn/ui primitives
β βββ Layout.tsx # Root layout (ThemeProvider, Header, Footer)
β βββ *.tsx # App components
βββ locales/ # i18n translations
β βββ en/ # English
β βββ es/ # Spanish
β βββ ... # 11 languages
βββ routes.tsx # SSG route definitions
βββ main.tsx # ViteReactSSG entry point
βββ __tests__/ # Tests (mirror structure)
8. Data Schema
Input Format (Instagram Data Download)
connections/followers_and_following/
βββ following.json # Accounts you follow
βββ followers_1.json # Your followers (may be split)
βββ close_friends.json # Close friends list (optional)
βββ pending_follow_requests.json # Pending requests (optional)
βββ recently_unfollowed_profiles.json # Recently unfollowed (optional)
βββ restricted_profiles.json # Restricted accounts (optional)
Core Calculations
- Set A: Usernames you follow (from
following.json) - Set B: Usernames who follow you (from
followers_*.json) - Not following back: A β B (excluding pending/restricted)
- Not followed back: B β A
- Mutuals: A β© B
9. Browser Compatibility
Supported Browsers
- Chrome: 90+ (recommended)
- Firefox: 88+
- Safari: 14+
- Edge: 90+
Required Features
- ES2020+ support
- IndexedDB
- Web Workers
- Service Workers (for PWA)
- CSS Grid/Flexbox
10. Security Considerations
Client-Side Security
- Input validation: Sanitize all user inputs
- XSS prevention: No dynamic HTML injection
- Content Security Policy: Strict CSP headers
- Subresource Integrity: For CDN resources
Data Privacy
- No data transmission: All processing local
- Anonymous analytics: Umami (no personal data)
- No cookies: No tracking cookies
- Secure defaults: Privacy-first configuration
11. PWA Configuration
Workbox Strategy
- Precache: 176 static assets
- Runtime caching: NetworkFirst for HTML pages
- Offline fallback: Cached app shell
Manifest
{
"name": "Instagram Unfollow Tracker",
"short_name": "Unfollow Radar",
"start_url": "/",
"display": "standalone",
"theme_color": "#000000"
}
12. Development Commands
npm run dev # Dev server (http://localhost:5173)
npm run build # Production build (SSG)
npm run test # Run tests (Vitest)
npm run test:coverage # Tests with 85% threshold
npm run lint:strict # ESLint (zero warnings)
npm run type-check # TypeScript validation
npm run code:check # lint:strict + type-check
This specification reflects v1.5.0 architecture. See CHANGELOG.md for version history.