22 KiB
Executable File
Dewemoji User Flow & UX Brief
Version: 1.0
Date: February 8, 2026
Purpose: Define seamless user experience across visitor → logged → paid states
Related Doc: dewemoji-direction-2026.md
Executive Summary
Dewemoji implements a hybrid quick-add flow where Personal users can add keywords instantly from emoji detail pages (80% of use cases) while maintaining full CRUD power in the dashboard (20% management). This creates a frictionless progression: discovery → personalization → habit, without disrupting the free user experience.
Core UX Principle: "Add keywords where you discover emojis, manage them where you organize."
1. User State Definitions
1.1 Visitor (Non-Logged)
Who: Anyone landing on dewemoji.com or using Chrome extension (no account)
Intent: Quick emoji search and usage
Experience: Zero friction, no login walls
1.2 Free User (Logged, No Subscription)
Who: Registered account, free tier
Intent: Exploring Personal features, considering upgrade
Experience: Full free features + gentle upgrade hints
1.3 Personal User (Logged, Paid)
Who: Active Personal subscription ($4.99/mo, $49/yr, or $99 lifetime)
Intent: Building and syncing personal keyword library
Experience: Full personalization power across all touchpoints
2. Primary User Flows
2.1 Discovery Flow (All Users)
User lands on dewemoji.com
↓
Search bar: "bekicot"
↓
Results page: 🐌 snail (public keywords: "snail, escargot")
↓
Click emoji → Detail page
↓
[State-dependent experience - see Section 3]
Key Insight: All users start here. Free discovery hooks them; Personal lets them own it.
2.2 Personalization Flow (Personal Users)
Primary: Quick Add from Detail Page (80% Usage)
Personal user searches "snail"
↓
Detail page shows:
┌─────────────────────────────────────┐
│ 🐌 Snail │
│ │
│ Public Keywords: snail, escargot │
│ │
│ Your Keywords: (none yet) │
│ [+ Add Your Keyword] │
└─────────────────────────────────────┘
↓
User clicks [+ Add Your Keyword]
↓
Inline modal appears:
┌─────────────────────────────────────┐
│ What do you call this emoji? │
│ ┌─────────────────────────────────┐│
│ │ bekicot, siput ││
│ └─────────────────────────────────┘│
│ Language: [Indonesian ▾] │
│ [Cancel] [Save Keyword] │
└─────────────────────────────────────┘
↓
Saves instantly → Toast: "Added 'bekicot' to your library ✓"
↓
Detail page now shows:
┌─────────────────────────────────────┐
│ 🐌 Snail │
│ │
│ Public Keywords: snail, escargot │
│ │
│ Your Keywords: │
│ • bekicot (ID) [edit] [×] │
│ • siput (ID) [edit] [×] │
│ [+ Add Another] │
└─────────────────────────────────────┘
↓
Next search: "bekicot" → 🐌 appears in results!
Why This Flow:
- Contextual: User is already looking at the emoji they want to name
- Fast: 2 clicks (add button → save), ~5 seconds total
- Immediate gratification: See change instantly in searches
- Low friction: No navigation away from discovery flow
Secondary: Dashboard Management (20% Usage)
When Used:
- Bulk editing/organizing keywords
- Reviewing all keywords at once
- Exporting/importing keyword libraries
- Fixing typos across multiple entries
User navigates to Dashboard → My Keywords tab
↓
Table view:
┌───────────────────────────────────────────────────────────┐
│ Emoji | Your Keywords | Lang | Actions │
├───────────────────────────────────────────────────────────┤
│ 🐌 | bekicot, siput | ID | [Edit] [Delete] │
│ 😊 | senyum, happy | ID | [Edit] [Delete] │
│ 🔥 | api, fire, lit | ID | [Edit] [Delete] │
└───────────────────────────────────────────────────────────┘
[+ Add Keyword] [Import JSON] [Export JSON] [Search/Filter]
↓
User clicks [+ Add Keyword]
↓
Modal: Search emoji picker → Select 🎉 → Add keywords
↓
Saves to table → Auto-sync to extension
Why Dashboard Exists:
- Power users want bulk operations
- Easy to review/audit entire library
- Import/export for backup
- Edit mistakes without finding emoji again
2.3 Upgrade Flow (Free → Personal)
Trigger Points
1. On Detail Page (Soft Prompt)
Free user on emoji detail page sees:
┌─────────────────────────────────────┐
│ 🐌 Snail │
│ │
│ Public Keywords: snail, escargot │
│ │
│ 💎 Want to add your own keywords? │
│ like 'bekicot' or 'siput'? │
│ [Upgrade to Personal →] │
└─────────────────────────────────────┘
2. On Search Miss (Strong Prompt)
User searches "bekicot" → No results
↓
Results page shows:
┌─────────────────────────────────────┐
│ No results for "bekicot" │
│ │
│ 💡 With Personal, you can create │
│ "bekicot → 🐌" and sync it │
│ everywhere. │
│ [Try Personal Free for 7 Days] │
└─────────────────────────────────────┘
3. From Extension (Contextual)
Extension user searches "bekicot" → Not found
↓
Extension shows inline banner:
"Add 'bekicot' to your library with Personal"
[Upgrade] button → Opens dewemoji.com/upgrade
4. From Dashboard (Clear Path)
Free user clicks "My Keywords" tab
↓
Shows empty state:
┌─────────────────────────────────────┐
│ 📚 Your Personal Keyword Library │
│ │
│ You have 0 keywords. │
│ │
│ Upgrade to Personal to: │
│ ✓ Add unlimited keywords │
│ ✓ Search in your language │
│ ✓ Sync across all devices │
│ │
│ [Start 7-Day Free Trial] │
└─────────────────────────────────────┘
3. State-Dependent UI Elements
3.1 Emoji Detail Page
Visitor (Non-Logged)
<!-- Public info only -->
<div class="emoji-detail">
<h1>🐌 Snail</h1>
<section class="public-keywords">
<h3>Keywords</h3>
<ul>
<li>snail</li>
<li>escargot</li>
<li>slow</li>
</ul>
</section>
<div class="cta-banner">
<p>💡 Want to search in your language?</p>
<button>Sign Up Free</button>
</div>
</div>
Free User (Logged)
<!-- Public info + muted personal section -->
<div class="emoji-detail">
<h1>🐌 Snail</h1>
<section class="public-keywords">
<h3>Keywords</h3>
<ul><li>snail</li><li>escargot</li></ul>
</section>
<section class="user-keywords muted">
<h3>Your Keywords</h3>
<div class="upgrade-overlay">
<p>💎 Add personal keywords like 'bekicot'</p>
<button class="upgrade-btn">Upgrade to Personal</button>
</div>
</section>
</div>
Personal User (Logged + Paid)
<!-- Full personalization active -->
<div class="emoji-detail">
<h1>🐌 Snail</h1>
<section class="public-keywords">
<h3>Public Keywords</h3>
<ul><li>snail</li><li>escargot</li><li>slow</li></ul>
</section>
<section class="user-keywords active">
<h3>Your Keywords</h3>
<ul class="keyword-list">
<li>
<span class="keyword">bekicot</span>
<span class="lang-tag">ID</span>
<button class="edit-btn">edit</button>
<button class="delete-btn">×</button>
</li>
<li>
<span class="keyword">siput</span>
<span class="lang-tag">ID</span>
<button class="edit-btn">edit</button>
<button class="delete-btn">×</button>
</li>
</ul>
<button class="add-keyword-btn">+ Add Keyword</button>
</section>
</div>
<!-- Add Keyword Modal (hidden until clicked) -->
<dialog id="add-keyword-modal">
<h3>What do you call this emoji?</h3>
<input type="text" placeholder="Enter keywords (comma-separated)" />
<select name="language">
<option value="en">English</option>
<option value="id">Indonesian</option>
<option value="ko">Korean</option>
<option value="ja">Japanese</option>
</select>
<div class="modal-actions">
<button class="cancel">Cancel</button>
<button class="save primary">Save Keyword</button>
</div>
</dialog>
3.2 Search Results Page
All Users (Public Results)
<div class="search-results">
<div class="emoji-card">
<span class="emoji">🐌</span>
<span class="name">Snail</span>
<span class="matched-keyword">snail</span>
</div>
</div>
Personal User (Blended Results)
<div class="search-results">
<!-- Private keyword match (appears first) -->
<div class="emoji-card user-match">
<span class="emoji">🐌</span>
<span class="name">Snail</span>
<span class="matched-keyword">
bekicot
<span class="badge personal">Your keyword</span>
</span>
<button class="quick-edit">Edit</button>
</div>
<!-- Public keyword matches (below) -->
<div class="emoji-card">
<span class="emoji">🐌</span>
<span class="name">Snail</span>
<span class="matched-keyword">snail</span>
</div>
</div>
3.3 Dashboard (Personal Users Only)
<div class="dashboard">
<nav class="tabs">
<button class="active">My Keywords</button>
<button>API Keys</button>
<button>Billing</button>
</nav>
<!-- My Keywords Tab -->
<div class="tab-content" id="keywords">
<div class="toolbar">
<button class="primary">+ Add Keyword</button>
<button>Import JSON</button>
<button>Export JSON</button>
<input type="search" placeholder="Search your keywords..." />
</div>
<table class="keyword-table">
<thead>
<tr>
<th>Emoji</th>
<th>Your Keywords</th>
<th>Language</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>🐌</td>
<td>bekicot, siput</td>
<td><span class="lang-tag">ID</span></td>
<td>
<button class="edit">Edit</button>
<button class="delete">Delete</button>
</td>
</tr>
<!-- More rows... -->
</tbody>
</table>
</div>
</div>
4. API Endpoints for UX Flows
4.1 Detail Page Quick Add
Fetch user's keywords for specific emoji:
GET /v1/emoji/:slug?include_user_keywords=true
Authorization: Bearer dew_abc123...
Response:
{
"emoji": {...},
"public_keywords": ["snail", "escargot"],
"user_keywords": [
{"id": "uk_1", "keyword": "bekicot", "lang": "id"},
{"id": "uk_2", "keyword": "siput", "lang": "id"}
]
}
Add keyword from detail page:
POST /v1/keywords/quick
Authorization: Bearer dew_abc123...
Body:
{
"emoji_slug": "snail",
"keywords": ["bekicot", "siput"],
"lang": "id"
}
Response:
{
"success": true,
"added": ["bekicot", "siput"],
"emoji_slug": "snail"
}
Edit single keyword:
PATCH /v1/keywords/:id
Authorization: Bearer dew_abc123...
Body:
{
"keyword": "bekicot kecil"
}
Delete keyword:
DELETE /v1/keywords/:id
Authorization: Bearer dew_abc123...
4.2 Dashboard Bulk Operations
List all user keywords:
GET /v1/keywords?page=1&limit=50
Authorization: Bearer dew_abc123...
Response:
{
"keywords": [
{
"id": "uk_1",
"emoji_slug": "snail",
"emoji": "🐌",
"keyword": "bekicot",
"lang": "id",
"created_at": "2026-02-08T10:30:00Z"
}
// ... more
],
"total": 47,
"page": 1
}
Bulk import:
POST /v1/keywords/import
Authorization: Bearer dew_abc123...
Body:
{
"keywords": [
{"emoji_slug": "snail", "keywords": ["bekicot"], "lang": "id"},
{"emoji_slug": "fire", "keywords": ["api"], "lang": "id"}
]
}
Response:
{
"imported": 2,
"skipped": 0,
"errors": []
}
Export:
GET /v1/keywords/export?format=json
Authorization: Bearer dew_abc123...
Response: (Download JSON file)
5. Frontend Implementation Specs
5.1 Emoji Detail Page Changes
File: /pages/emoji/[slug].js (or equivalent)
Required Elements:
-
User keywords section (Personal only)
- Fetch on page load:
GET /v1/emoji/:slug?include_user_keywords=true - Show list of user's keywords with edit/delete buttons
- Show "Add Keyword" button
- Fetch on page load:
-
Add keyword modal
- Input: comma-separated keywords
- Dropdown: language selector (EN, ID, KO, JA, etc.)
- Save button → POST
/v1/keywords/quick - Success → update UI without page reload
-
Inline edit
- Click "edit" → input becomes editable
- Save → PATCH
/v1/keywords/:id - Cancel → revert
-
Inline delete
- Click "×" → confirm modal
- Yes → DELETE
/v1/keywords/:id→ remove from UI
State Management:
const [userKeywords, setUserKeywords] = useState([]);
const [isPersonal, setIsPersonal] = useState(false);
// On mount
useEffect(() => {
if (user?.tier === 'personal') {
fetchUserKeywords(emojiSlug);
}
}, [emojiSlug, user]);
// Add keyword
const handleAddKeyword = async (keywords, lang) => {
const result = await api.post('/keywords/quick', {
emoji_slug: emojiSlug,
keywords: keywords.split(',').map(k => k.trim()),
lang
});
if (result.success) {
fetchUserKeywords(emojiSlug); // Refresh list
toast.success('Keywords added!');
}
};
5.2 Dashboard Implementation
File: /pages/dashboard.js
Tabs:
- My Keywords (default)
- API Keys
- Billing
My Keywords Tab Components:
// Main table component
<KeywordTable
keywords={keywords}
onEdit={handleEdit}
onDelete={handleDelete}
onSearch={handleSearch}
/>
// Toolbar actions
<Toolbar>
<AddKeywordButton onClick={openModal} />
<ImportButton />
<ExportButton />
<SearchInput onChange={handleSearch} />
</Toolbar>
// Add keyword modal (with emoji picker)
<AddKeywordModal
onSave={handleAddKeyword}
onCancel={closeModal}
/>
Features:
- Pagination (50 per page)
- Search/filter by keyword or emoji
- Bulk select + delete
- Sort by: date added, emoji name, language
5.3 Upgrade Flow Pages
File: /pages/upgrade.js or /pages/pricing.js
Components:
-
Feature comparison table
<table class="pricing-comparison"> <tr> <th>Feature</th> <th>Free</th> <th>Personal</th> </tr> <tr> <td>Public emoji search</td> <td>✅ Unlimited</td> <td>✅ Unlimited</td> </tr> <tr> <td>Private keywords</td> <td>❌</td> <td>✅ Unlimited</td> </tr> <!-- ... --> </table> -
Pricing cards
<div class="pricing-cards"> <div class="card"> <h3>Monthly</h3> <p class="price">$4.99/mo</p> <button>Subscribe</button> </div> <div class="card recommended"> <span class="badge">Best Value</span> <h3>Annual</h3> <p class="price">$49/year</p> <p class="savings">Save 17%</p> <button>Subscribe</button> </div> <div class="card"> <h3>Lifetime</h3> <p class="price">$99 once</p> <button>Buy Now</button> </div> </div> -
Stripe checkout integration
const handleCheckout = async (plan) => { const session = await api.post('/stripe/checkout', { plan, // 'monthly' | 'annual' | 'lifetime' success_url: `${baseUrl}/dashboard?upgraded=true`, cancel_url: `${baseUrl}/upgrade` }); window.location.href = session.url; };
6. UX Copy & Messaging
6.1 Upgrade Prompts
Soft (Detail Page):
💡 Want to search in your language? Add personal keywords like 'bekicot' for 🐌
[Upgrade to Personal →]
Strong (Search Miss):
No results for "bekicot"
💎 Create your own keywords with Personal
Add "bekicot → 🐌" and sync it across all devices
[Try Free for 7 Days]
Empty State (Dashboard):
📚 Your Personal Keyword Library
You have 0 keywords.
Upgrade to Personal to:
✓ Add unlimited keywords
✓ Search in your language
✓ Sync across all devices[Start 7-Day Free Trial]
6.2 Success Messages
After adding keyword:
✓ Added "bekicot" to your library
After editing:
✓ Keyword updated
After deleting:
✓ Keyword removed
After upgrade:
🎉 Welcome to Personal! Start adding your keywords.
6.3 Error Messages
Duplicate keyword:
⚠️ You already have "bekicot" for this emoji
Network error:
❌ Couldn't save. Check your connection and try again.
Unauthorized:
🔒 Please log in to add personal keywords
7. Mobile Considerations
7.1 Detail Page on Mobile
- Add keyword button: sticky bottom bar (always visible)
- Modal: full-screen on mobile (easier typing)
- Keyword list: swipeable cards (swipe left to delete)
7.2 Dashboard on Mobile
- Tabs: horizontal scroll or bottom nav
- Table: card view (stack columns vertically)
- Actions: swipe gestures (edit/delete)
8. Performance & Optimization
8.1 Caching Strategy
Client-side:
- Cache user keywords in localStorage/sessionStorage
- Only refetch when:
- User adds/edits/deletes
- Page reload after 5 minutes
API-side:
- ETag support (already implemented)
- Cache user keywords per user_id (Redis)
- Invalidate on mutation
8.2 Loading States
Detail page:
Loading user keywords...
└─ Skeleton: [▯▯▯▯] [▯▯▯] [▯▯▯▯▯]
Dashboard:
Loading your library...
└─ Table skeleton (5 rows)
9. Analytics & Tracking
Key Events to Track
Discovery:
emoji_viewed(slug, user_tier)public_search(query, has_results)search_miss(query) → upgrade opportunity
Personalization:
keyword_added(method: 'quick_add' | 'dashboard')keyword_editedkeyword_deletedkeywords_exported
Conversion:
upgrade_prompt_shown(location: 'detail' | 'search' | 'dashboard')upgrade_clicked(location)trial_startedsubscription_created(plan: 'monthly' | 'annual' | 'lifetime')
Usage:
private_keyword_searched(query, has_results)extension_synced(keyword_count)
10. Testing Checklist
10.1 User State Tests
- Visitor can search and view emoji details (no login)
- Visitor sees upgrade CTA (non-intrusive)
- Free user sees muted keyword section
- Free user can't add keywords without upgrade
- Personal user sees active keyword section
- Personal user can add keywords from detail page
- Personal user can edit/delete keywords inline
10.2 Flow Tests
- Quick add: add keyword → appears in list immediately
- Quick add: new keyword searchable instantly
- Dashboard: bulk add works correctly
- Dashboard: export downloads valid JSON
- Dashboard: import restores keywords
- Search: private keywords appear first in results
- Search: private keywords have "Your keyword" badge
10.3 Edge Cases
- Add duplicate keyword → shows error
- Delete last keyword for emoji → removes emoji from search
- Offline: queue mutations, sync when online
- Rate limit: show friendly error
- API error: show retry option
11. Implementation Priority
Phase 1: Detail Page Quick Add (Week 1)
- Add user keywords section to detail page
- Build add keyword modal
- Implement POST /keywords/quick endpoint
- Add inline edit/delete
Phase 2: Search Blending (Week 1)
- Update search to blend private + public
- Add "Your keyword" badge to results
- Show private matches first
Phase 3: Dashboard (Week 2)
- Build My Keywords tab with table
- Add bulk actions (import/export)
- Implement search/filter
Phase 4: Upgrade Flow (Week 2)
- Build upgrade prompts (detail, search miss, dashboard)
- Create pricing page
- Integrate Stripe checkout
12. Open Questions & Future Enhancements
- Keyword suggestions: Should we suggest keywords based on emoji name/category?
- Keyboard shortcuts: Quick add keyword with hotkey (Ctrl+K)?
- Recently added: Show "Recently added keywords" widget on dashboard?
- Keyword collections: Group keywords by category/theme?
- Sharing: Allow users to share their keyword libraries with friends?
End of UX Brief
This document defines the seamless user experience for Dewemoji's core value proposition: instant, contextual personalization. Implementation should prioritize the detail page quick-add flow (80% of usage) while building dashboard for power users (20%).