Four production-ready Next.js 14 blocks covering analytics tracking, real-time notifications, rate limiting, and full-text search. Drop them in individually or use all four together.
Stack: Next.js 14 · TypeScript · Supabase · PostHog · Upstash Redis
| Folder | Block | What it does |
|---|---|---|
analytics/ |
Analytics Tracker | PostHog wrapper with typed events, page tracking hook, user identification, self-hosted Supabase fallback, funnel/retention SQL queries |
notifications/ |
Notifications System | Supabase Realtime in-app notifications, unread badge, mark-as-read, archive, bell dropdown component, Web Push subscribe/unsubscribe |
ratelimit/ |
Rate Limiting | Sliding window in-memory store with Upstash Redis upgrade path, per-IP and per-user strategies, pre-tuned configs for auth/purchase/AI/webhook routes |
search/ |
Full-Text Search | PostgreSQL tsvector + GIN index, websearch query format, autocomplete, facet counts, highlight extraction, debounced React hook with pagination |
Each folder contains one .ts / .tsx file and one README.md with its own setup instructions and SQL migration.
npm install next@14 react react-dom typescript @supabase/supabase-jsEnvironment variables used across all four blocks:
# Supabase (all blocks)
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY= # notifications, analytics (server-side)
# Analytics block
NEXT_PUBLIC_POSTHOG_KEY=
NEXT_PUBLIC_POSTHOG_HOST=https://app.posthog.com
# Rate Limiting block (production only)
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
# Rate Limiting — withAuthRateLimit only
NEXTAUTH_SECRET=
# Notifications block — Web Push only
NEXT_PUBLIC_VAPID_PUBLIC_KEY= # generate: npx web-push generate-vapid-keys
The profiles table is assumed to already exist (created by the Auth block). If starting fresh, run the Auth block migration first.
Then run each block's migration independently — they don't depend on each other:
analytics/README.md → creates analytics_events
notifications/README.md → creates notifications, push_subscriptions
search/README.md → adds fts column + GIN index to your target table
The Rate Limiting block has no database migration — it uses in-memory store by default.
In the Supabase dashboard: Database → Replication → supabase_realtime → Add table → notifications
# Analytics
npm install posthog-js
# Rate Limiting (production)
npm install @upstash/ratelimit @upstash/redisAll four blocks are standalone — none depends on another. The only shared external dependency is Supabase.
analytics ← standalone; PostHog optional (has Supabase fallback)
notifications ← requires profiles table (Auth block)
ratelimit ← standalone in dev; Upstash Redis for production
search ← requires a table with a tsvector column + GIN index
marrowstack-v2/
├── analytics/
│ ├── analytics.ts
│ └── README.md
├── notifications/
│ ├── notifications.ts
│ └── README.md
├── ratelimit/
│ ├── ratelimit.ts
│ └── README.md
├── search/
│ ├── search.ts
│ └── README.md
└── README.md