Skip to content

Step 6: Add Success Feedback

Time: ~5 minutes | Type: UX | Concepts: User feedback, toast integration

What We're Building

Adding success toast notifications to all CRUD operations:

  • "Todo created!" after creating
  • "Changes saved!" after updating
  • "Todo deleted!" after deleting

The Prompt for AI

💡 Ask AI to help you add success toasts:

I have a useToast hook that provides showToast(message, type).

I need to add success toasts to:
1. CreateTodoPage - after successful creation
2. EditTodoPage - after successful update
3. Delete function - after successful deletion

For each operation:
- Show appropriate success message
- Use 'success' type
- Toast should appear before redirect

Can you show me where and how to add showToast() calls?

Wait for AI's response, then apply the changes.

Part 1: Add Success Toast to Create

Find your CreateTodoPage component:

Before (no success feedback):

tsx
async function handleSubmit(e) {
  e.preventDefault();
  setLoading(true);
  try {
    await addDoc(collection(db, 'todos'), {
      title,
      completed: false,
      userId: currentUser.uid,
      createdAt: Timestamp.now()
    });
    router.push('/todos');  // Just redirects
  } catch (error) {
    console.error('Failed to create todo:', error);
  } finally {
    setLoading(false);
  }
}

After (with success toast):

tsx
import { useToast } from '../hooks/useToast';

function CreateTodoPage() {
  const { showToast } = useToast();  // Add this
  const [loading, setLoading] = useState(false);

  async function handleSubmit(e) {
    e.preventDefault();
    setLoading(true);
    try {
      await addDoc(collection(db, 'todos'), {
        title,
        completed: false,
        userId: currentUser.uid,
        createdAt: Timestamp.now()
      });
      showToast('Todo created!', 'success');  // SUCCESS: Show success toast
      router.push('/todos');
    } catch (error) {
      console.error('Failed to create todo:', error);
      showToast('Could not create todo. Please try again.', 'error');  // Error toast
    } finally {
      setLoading(false);
    }
  }

  // ... rest of component
}

Key changes:

  • Import and call useToast()
  • Show success toast after successful creation
  • Show error toast on failure

Part 2: Add Success Toast to Update

Find your EditTodoPage component:

tsx
import { useToast } from '../hooks/useToast';

function EditTodoPage() {
  const { showToast } = useToast();
  const [loading, setLoading] = useState(false);

  async function handleSubmit(e) {
    e.preventDefault();
    setLoading(true);
    try {
      await updateDoc(doc(db, 'todos', todoId), {
        title,
        completed
      });
      showToast('Changes saved!', 'success');  // SUCCESS: Show success toast
      router.push('/todos');
    } catch (error) {
      console.error('Failed to update todo:', error);
      showToast('Could not save changes. Please try again.', 'error');
    } finally {
      setLoading(false);
    }
  }

  // ... rest of component
}

Part 3: Add Success Toast to Delete

Find your delete function (likely in TodosPage or TodoItem component):

Before (no success feedback):

tsx
async function handleDelete(todoId: string) {
  if (confirm('Delete this todo?')) {
    await deleteDoc(doc(db, 'todos', todoId));
    // Refresh list
  }
}

After (with success toast):

tsx
import { useToast } from '../hooks/useToast';

function TodosPage() {
  const { showToast } = useToast();

  async function handleDelete(todoId: string) {
    if (confirm('Delete this todo?')) {
      try {
        await deleteDoc(doc(db, 'todos', todoId));
        showToast('Todo deleted!', 'success');  // SUCCESS: Show success toast
        // Refresh list (re-fetch or filter state)
      } catch (error) {
        console.error('Failed to delete todo:', error);
        showToast('Could not delete todo. Please try again.', 'error');
      }
    }
  }

  // ... rest of component
}

Part 4: Update Toggle Complete (Optional)

If you have a "mark complete" checkbox:

tsx
async function handleToggleComplete(todoId: string, currentStatus: boolean) {
  try {
    await updateDoc(doc(db, 'todos', todoId), {
      completed: !currentStatus
    });
    // Optional: Show toast for this too
    showToast(
      !currentStatus ? 'Todo completed!' : 'Todo marked incomplete',
      'success'
    );
  } catch (error) {
    console.error('Failed to update status:', error);
    showToast('Could not update status. Please try again.', 'error');
  }
}

Note: For checkboxes, toasts are optional (visual change is feedback enough).

Verification

Test all success toasts:

Test 1: Create Success

  1. Go to /todos/new
  2. Fill form: title "Test toast"
  3. Click "Create Todo"
  4. Expected:
    • [ ] Loading state shows
    • [ ] Green toast appears: "Todo created!"
    • [ ] Redirects to list
    • [ ] New todo appears in list
    • [ ] Toast auto-dismisses after 3s

Test 2: Update Success

  1. Click "Edit" on a todo
  2. Change title to "Updated with toast"
  3. Click "Save Changes"
  4. Expected:
    • [ ] Loading state shows
    • [ ] Green toast: "Changes saved!"
    • [ ] Redirects to list
    • [ ] Updated title appears
    • [ ] Toast auto-dismisses

Test 3: Delete Success

  1. Click "Delete" on a todo
  2. Confirm deletion
  3. Expected:
    • [ ] Green toast: "Todo deleted!"
    • [ ] Todo disappears from list
    • [ ] Toast auto-dismisses

Test 4: Error Handling

  1. Turn off internet/Wi-Fi
  2. Try to create a todo
  3. Expected:
    • [ ] Red toast appears
    • [ ] Message: "Could not create todo. Please try again."
    • [ ] Stays on form (doesn't redirect)
    • [ ] Input data preserved
    • [ ] Toast auto-dismisses after 5s

Common Issues

Toast Not Appearing

Problem: useToast not called or ToastProvider missing.

Fix:

  • Check const { showToast } = useToast(); at top of component
  • Verify ToastProvider wraps your app
  • Check browser console for errors

Toast Shows But Wrong Message

Problem: Copy-paste error.

Fix: Check the message in showToast():

tsx
showToast('Todo created!', 'success');  // SUCCESS: Correct
showToast('Changes saved!', 'success'); // SUCCESS: For update
showToast('Todo deleted!', 'success');  // SUCCESS: For delete

Toast Shows After Redirect

Problem: Toast appears then immediately disappears.

Fix: Toast shows in current page context. If you redirect immediately, it might not be visible long enough. This is usually fine, but if you want delay:

tsx
showToast('Todo created!', 'success');
await new Promise(resolve => setTimeout(resolve, 500));  // Wait 500ms
router.push('/todos');

Note: Usually not necessary. The toast will appear briefly before redirect.

Multiple Toasts on Error

Problem: Toast shows multiple times.

Fix: Make sure try/catch doesn't nest. Only one showToast per error:

tsx
try {
  await operation();
  showToast('Success!', 'success');
} catch (error) {
  showToast('Error message', 'error');  // Only show once
}

Understanding Check

Before moving on, make sure you understand:

💡 Ask yourself:

  1. When should I show success toasts? (After every successful CRUD operation)
  2. What message for creating a todo? ("Todo created!")
  3. Should I show toast before or after redirect? (Before, so it's visible)
  4. What type for success toasts? ('success')
  5. Should I show toasts for checkbox toggles? (Optional, visual feedback is enough)
  6. What if the operation fails? (Show error toast with helpful message)

What You Learned

At this point you should have:

  • ✅ Success toast after creating todo
  • ✅ Success toast after updating todo
  • ✅ Success toast after deleting todo
  • ✅ Error toasts for all failures
  • ✅ User always knows if action succeeded
  • ✅ Professional feedback for all operations
  • ✅ Better error handling with user-friendly messages

Next Step

Now that we have success feedback, let's improve error messages to be more user-friendly:

Step 7: Improve Error Messages →

Built for learning | Open source on GitHub