Step 1: Understanding UX
Time: ~5 minutes | Type: Learning | Concepts: User experience, loading states, feedback
What We're Learning
Before we add polish to your app, we need to understand what makes software feel professional and why every action needs clear feedback.
Before You Code: Ask AI First
💡 Interactive Learning:
Open your AI assistant and ask these questions. Really read the answers!
What is user experience (UX) in software? Why do loading states matter? What happens without them? What is user feedback in apps? Can you give examples? What makes an error message good vs bad? Why should buttons be disabled during loading? What are toast notifications and when should I use them? What could go wrong if I don't provide feedback for user actions?
What you should learn:
- UX is how users feel when using your app
- Loading states prevent confusion and double-submissions
- Every action needs visible feedback
- Good error messages are friendly, specific, and helpful
- Disabled buttons prevent accidental double-clicks
- Toast notifications provide non-blocking feedback
- Without feedback, users are confused and frustrated
Understanding the Problem
After asking AI, make sure you understand these scenarios:
Scenario 1: No Loading State
User clicks "Create Todo"
↓
[Button stays normal]
↓
User thinks: "Did it work? Should I click again?"
↓
User clicks again (creating duplicate!)
↓
Two todos appearProblem: User doesn't know the first click is being processed.
Solution: Show spinner, disable button, provide feedback.
Scenario 2: Bad Error Message
User submits form with empty title
↓
Error: "INVALID_INPUT_ERR_400"
↓
User thinks: "What does that mean? What do I fix?"Problem: Technical jargon doesn't help users.
Solution: "Title is required. Please enter at least 1 character."
Scenario 3: No Success Feedback
User clicks "Delete"
↓
Todo disappears from list
↓
User thinks: "Did it delete or just hide? Did it work?"Problem: User isn't sure if action succeeded.
Solution: Show toast: "Todo deleted successfully!"
Good UX Principles
1. Immediate Feedback
Every user action should have immediate visual feedback.
Examples:
- Click button → Button shows spinner
- Submit form → Button disables, shows "Saving..."
- Hover button → Background color changes
- Focus input → Blue outline appears
2. Clear Communication
Tell users what's happening in plain English.
Examples:
- Not: "Processing..."
- Better: "Creating your todo..."
- Not: "Error occurred"
- Better: "Title is required"
3. Prevent Mistakes
Stop users from doing things they don't mean to do.
Examples:
- Disable submit button during loading
- Ask "Are you sure?" before deleting
- Validate forms before submitting
- Show character count for limited inputs
4. Handle Errors Gracefully
When things go wrong, help users fix it.
Examples:
- Show what went wrong
- Explain how to fix it
- Keep their data (don't clear form)
- Allow retry
Examples: Before and After
Example 1: Creating a Todo
❌ Before (no UX):
async function createTodo() {
await addDoc(collection(db, 'todos'), { title, userId });
router.push('/todos');
}Problems:
- No loading state
- No success message
- No error handling
- Button stays clickable (can double-submit)
✅ After (good UX):
async function createTodo() {
setLoading(true); // Show spinner
try {
await addDoc(collection(db, 'todos'), { title, userId });
showToast('Todo created!', 'success'); // Success feedback
router.push('/todos');
} catch (error) {
console.error(error);
showToast('Could not create todo. Please try again.', 'error'); // Friendly error
} finally {
setLoading(false); // Hide spinner
}
}Improvements:
- Shows loading state
- Success toast notification
- Friendly error message
- Button disabled during loading
Example 2: Delete Button
❌ Before (no confirmation):
<button onClick={() => deleteTodo(todo.id)}>
Delete
</button>Problem: Accidental deletes, no confirmation
✅ After (with confirmation):
<button onClick={() => {
if (confirm('Delete this todo?')) {
deleteTodo(todo.id);
}
}}>
Delete
</button>Improvement: Asks before deleting
Understanding Check
Before moving on, make sure you can answer these:
💡 Ask yourself (or ask AI if unsure):
- What happens if a button doesn't show a loading state? (Users might click multiple times)
- Why should error messages be in plain English? (Technical jargon confuses users)
- When should I show a success message? (After every successful action)
- What's wrong with the error "ERR_500"? (Not user-friendly, no context, no solution)
- Why disable buttons during loading? (Prevent double-submissions)
- What are toast notifications? (Small, temporary messages that auto-dismiss)
Expected answers:
- Users confused, may submit multiple times
- Users need to understand what went wrong and how to fix it
- After create, update, delete, or any action
- It's technical, doesn't explain problem or solution
- Prevents accidental duplicate submissions
- Non-blocking, temporary feedback messages
What You Learned
At this point you should understand:
- ✅ What user experience (UX) means
- ✅ Why loading states prevent confusion
- ✅ Why every action needs feedback
- ✅ What makes error messages good vs bad
- ✅ Why buttons should disable during loading
- ✅ What toast notifications are
- ✅ The difference between polished and unpolished apps
Next Step
Now that you understand UX principles, let's create a Spinner component to show loading states: