Day 7: User Identification – Anonymous vs Identified Users
For the past six days, I've been tracking events in TaskFlow without knowing who my users actually are. PostHog was capturing everything—task creation, completion, filtering—but all I could see were cryptic IDs like 019b88cf-ed22-75f3-b1f4-195b0718a968.
Today, I needed to fix that. I needed to connect these anonymous actions to real people.
The Problem: IDs Without Identity
I opened PostHog's Activity tab and saw dozens of events. Each one had a person ID attached—long strings of letters and numbers. PostHog was tracking users, but I couldn't tell who they were.
Then I checked the Persons tab, expecting to see a list of my users. Instead: "There are no matching persons for this query."
Wait. If the Activity tab shows person IDs, why is the Persons tab empty?
This is where I realized I was dealing with anonymous users—PostHog's default way of tracking people before you identify them.


How PostHog Tracks Anonymous Users
According to the documentation, when someone visits your app, PostHog automatically assigns them an anonymous ID. This ID gets stored locally (in cookies or localStorage), which means PostHog can track them across sessions—even if they close the browser and come back later.
The anonymous ID persists as long as:
They're using the same browser
They haven't cleared their cookies
They're on the same device
For TaskFlow, this meant every user got a unique ID like 019b88cf-ed22-75f3-b1f4-195b0718a968, and all their events—task creation, filtering, session replay—were linked to that ID.
The limitation: I couldn't tell who these people were. Was 019b88cf a power user creating 20 tasks, or someone who tried the app once and left? No idea.
The Solution: posthog.identify()
The docs explained that to link events to actual people, I needed to call posthog.identify() with a unique identifier—typically a user's email or database ID.
Since TaskFlow was just a simple test app, I didn't need a full authentication system. But I did need a way to identify users. So I built a basic login form.
Building the Login Flow
I created a simple email-only login (no password—this is just for testing PostHog, not production security). Here's what happens:
1. User enters their email
const handleLogin = (e: React.FormEvent) => {
e.preventDefault();
if (!email) return;
// Save user to localStorage (simulating login)
const user = auth.login(email, name || undefined);
// Identify user in PostHog
posthog.identify(email, {
email: email,
name: name || undefined,
identified_at: new Date().toISOString(),
});
// Capture login event
posthog.capture("user_logged_in", {
email: email,
});
// Redirect to tasks
router.push("/tasks");
};
2. PostHog links the anonymous ID to the email
When I call posthog.identify(email), PostHog connects all the previous anonymous events to this email. The user's history doesn't disappear—it gets merged into their new identified profile.
3. Future events are now linked to the email
Any tasks created, completed, or deleted after login are automatically associated with the user's email instead of the anonymous ID.
The Before and After
Before identification:
Activity tab: Events with anonymous IDs (
019b88cf-ed22-75f3...)Persons tab: Empty (or only anonymous users)
No way to distinguish users
Can't reach out to specific people
After identification:
Activity tab: Events now linked to
user@example.comPersons tab: Shows identified users with their emails
Can see who's a power user vs casual visitor
Can segment by user properties

Handling Logout: posthog.reset()
One thing the docs emphasize: if users share devices, you need to call posthog.reset() when they log out. Otherwise, the next user's events get mixed with the previous user's profile.
Here's what I implemented:
const handleLogout = () => {
// 1. Capture logout event (before reset)
posthog.capture("user_logged_out");
// 2. Reset PostHog (unlink future events)
posthog.reset();
// 3. Clear localStorage
auth.logout();
// 4. Redirect to login
router.push("/");
};
Calling posthog.reset() does two things:
Unlinks future events from the current user
Generates a new anonymous ID for the next session
This is crucial for shared computers. Without it, if Sarah logs out and Tom logs in, Tom's events might get attributed to Sarah until Tom identifies himself.
What I Learned About When to Identify
The docs recommend calling posthog.identify() "as soon as you're able to"—typically right after login or signup.
But there's a tradeoff: anonymous events are 4x cheaper than identified events in PostHog's pricing.
For TaskFlow, I'm identifying users at login because:
I want to know who's using the app
I need to test user identification for this series
TaskFlow is a small demo app (cost isn't a concern)
For a production app, I'd think more carefully:
Maybe only identify paying users
Or identify after a user completes X actions (showing commitment)
Or keep casual browsers anonymous until they sign up
The key insight: identification isn't all-or-nothing. You can run a mix of anonymous and identified users based on what makes sense for your product.
Up Next: Day 8
So far we've covered:
Day 1: What is PostHog? ✅
Day 2: Setup (SSR bug) ✅
Day 3: Empty dashboard ✅
Day 4: Toolbar (visual event tracking) ✅
Day 5: Custom events (AI magic) ✅
Day 6: Session replay (UX goldmine) ✅
Day 7: User identification (anonymous → identified) ✅
Coming up:
Day 8: Funnels – Where users drop off (and why)
Day 9+: More features...
Follow along with the series:
- 🎥 TikTok: [https://www.tiktok.com/@hey_techys?is_from_webapp=1&sender_device=pc]
- 📸 Instagram: [https://www.instagram.com/hey_techys]
- 💼 LinkedIn: [https://www.linkedin.com/in/onyenekwe-elizabeth-46a467183/]
- 🐦 Twitter/X: [https://x.com/ElizabethOnyen6]
Byeeeeeeee!!!!
- Lizzy