Mở đầu — vì sao bài này quan trọng
Hầu hết các nhà thiết kế dành 90% thời gian để chăm chút cho "happy path" — con đường lý tưởng khi mọi thứ diễn ra suôn sẻ: người dùng có sẵn dữ liệu, gõ đúng thông tin, mạng ổn định, server phản hồi nhanh. Nhưng trong thực tế, một phần rất lớn trải nghiệm của người dùng lại diễn ra ở những trạng thái "không lý tưởng": màn hình trống trơn khi vừa đăng ký, thông báo lỗi khi nhập sai, danh sách rỗng khi tìm kiếm không ra kết quả, hay giao diện vỡ khi tên người dùng dài bất thường.
Đây chính là ba nhóm trạng thái mà bài học hôm nay tập trung: empty states (trạng thái rỗng), error states (trạng thái lỗi) và edge cases (trường hợp biên). Chúng thường bị bỏ quên vì trong Figma chúng ta luôn đổ đầy dữ liệu mẫu đẹp đẽ, còn khi demo thì ai cũng chọn kịch bản thuận lợi nhất. Hậu quả là người dùng thật — đặc biệt là người dùng mới, người dùng nhập sai, hay người dùng gặp sự cố — lại rơi vào những màn hình vô hồn, khó hiểu, thậm chí khiến họ bỏ đi ngay.
Điều đáng nói là các trạng thái này lại xuất hiện đúng vào thời điểm quyết định. First-time user gặp màn hình rỗng chính là khoảnh khắc họ quyết định có gắn bó với sản phẩm hay không. Một người dùng gặp lỗi thanh toán chính là lúc bạn dễ mất tiền và mất niềm tin nhất. Thiết kế tốt những trạng thái này không phải là "trang trí thêm" — nó là phần cốt lõi giúp giữ chân người dùng và bảo vệ chỉ số chuyển đổi. Trong bài này, chúng ta sẽ học cách biến những màn hình "chết" thành những điểm chạm có ích, có định hướng và có cảm xúc.
Khái niệm cốt lõi
Empty state — trạng thái rỗng
Empty state là trạng thái khi một khu vực giao diện đáng lẽ chứa nội dung nhưng lại chưa có gì để hiển thị. Có ba loại empty state chính, và việc phân biệt chúng cực kỳ quan trọng vì cách xử lý mỗi loại hoàn toàn khác nhau:
- First-use empty state (lần đầu sử dụng): Người dùng mới, chưa có dữ liệu nào. Đây là loại quan trọng nhất vì nó gắn liền với onboarding và ấn tượng đầu tiên.
- User-cleared empty state (đã dọn sạch): Người dùng đã hoàn thành hết công việc — ví dụ inbox đã đọc hết email, danh sách todo đã xong. Đây thường là trạng thái tích cực, đáng chúc mừng.
- No-results empty state (không có kết quả): Kết quả tìm kiếm hoặc bộ lọc trả về rỗng. Cần giúp người dùng điều chỉnh lại truy vấn.
- Chức năng này để làm gì? — Giải thích ngắn gọn giá trị của tính năng, thay vì chỉ hiển thị "Chưa có dữ liệu".
- Tôi nên làm gì tiếp theo? — Cung cấp một CTA (call-to-action) rõ ràng để thêm mục đầu tiên, ví dụ nút "Tạo dự án đầu tiên".
- Làm sao để bắt đầu nhanh? — Cho phép dùng dữ liệu mẫu (sample data) hoặc template để người dùng thấy ngay kết quả mà không phải nhập từ đầu.
Error state — trạng thái lỗi
Error state xuất hiện khi có gì đó không diễn ra như mong đợi. Nguyên tắc vàng ở đây đến từ heuristic thứ 9 của Nielsen (mà chúng ta đã học ở Bài 37): "Help users recognize, diagnose, and recover from errors" — giúp người dùng nhận ra, chẩn đoán và khắc phục lỗi. Một thông báo lỗi tốt cần:
- Diễn đạt bằng ngôn ngữ con người, không dùng mã lỗi kỹ thuật kiểu "Error 500" hay "NullPointerException".
- Nói rõ chuyện gì đã xảy ra và tại sao, một cách ngắn gọn.
- Chỉ ra cách khắc phục — bước tiếp theo cụ thể người dùng có thể làm.
- Không đổ lỗi cho người dùng. Tránh từ ngữ như "Bạn đã nhập sai"; thay vào đó dùng "Số điện thoại cần có 10 chữ số".
- Đặt lỗi đúng vị trí. Lỗi ở trường form nên hiển thị ngay cạnh trường đó (inline), không phải ở đầu trang.
Edge case — trường hợp biên
Edge case là những tình huống hiếm gặp hoặc cực đoan mà thiết kế "bình thường" không lường trước. Đây là nơi giao diện dễ vỡ nhất:
- Dữ liệu dài hoặc ngắn bất thường: Tên "Nguyễn" và tên "Nguyễn Trần Hoàng Minh Khánh Duy" hiển thị rất khác nhau. Email dài tràn khỏi ô, tiêu đề sản phẩm quá dài đẩy vỡ layout.
- Số lượng cực đoan: 0 mục, 1 mục, hay 10.000 mục — mỗi mức đều cần cân nhắc (ví dụ số hiển thị "1.000.000" hay giỏ hàng có 999+ sản phẩm).
- Kết nối chậm/gián đoạn: Trạng thái loading kéo dài, ảnh chưa tải, offline.
- Nội dung do người dùng tạo: Người dùng không tải ảnh đại diện, không điền tên, dán emoji vào ô tên.
- Đa ngôn ngữ và múi giờ: Tiếng Việt có dấu, tiếng Đức dài hơn tiếng Anh ~30%, số tiền VND khác USD về định dạng.
Tình huống thực tế
Ví dụ 1: Ứng dụng quản lý chi tiêu MoMo và màn hình đầu tiên
Hãy tưởng tượng một tính năng "Quản lý chi tiêu" mới ra mắt trên một ví điện tử phổ biến tại Việt Nam. Ở phiên bản đầu, khi người dùng mở tab này lần đầu, họ chỉ thấy một biểu đồ trống với dòng chữ "Chưa có dữ liệu chi tiêu". Kết quả đo được: chỉ khoảng 8% người dùng quay lại tab này lần thứ hai. Người dùng không hiểu tính năng làm được gì và cũng không biết bắt đầu từ đâu.
Nhóm thiết kế làm lại empty state theo đúng ba câu hỏi. Màn hình mới có: một minh họa thân thiện, dòng tiêu đề "Theo dõi tiền của bạn đi đâu mỗi tháng", một câu mô tả ngắn về lợi ích, một nút CTA nổi bật "Ghi khoản chi đầu tiên", và quan trọng nhất — một liên kết "Xem với dữ liệu mẫu" cho phép người dùng lướt thử giao diện với số liệu giả lập của một tháng mẫu. Sau khi triển khai, tỷ lệ người dùng thực hiện hành động đầu tiên (ghi một khoản chi) tăng từ khoảng 12% lên gần 34%.
Bài học: Empty state của first-time user không phải là chỗ báo "trống", mà là cơ hội bán giá trị và mời gọi hành động. Tùy chọn dữ liệu mẫu giúp người dùng "nếm thử" trước khi phải bỏ công nhập liệu — giảm rào cản khởi động đáng kể.
Ví dụ 2: Tiki và thông báo lỗi khi thanh toán thất bại
Trên một sàn thương mại điện tử lớn, khâu thanh toán là nơi nhạy cảm nhất. Giả sử ở phiên bản cũ, khi giao dịch thẻ bị từ chối, người dùng nhận được thông báo: "Giao dịch thất bại. Mã lỗi: 05". Người dùng hoang mang — họ không biết tiền có bị trừ không, không biết lỗi do mình hay do hệ thống, và không biết phải làm gì. Đội chăm sóc khách hàng ghi nhận lượng lớn cuộc gọi hỏi về đúng tình huống này, còn tỷ lệ bỏ giỏ hàng ở bước cuối tăng cao.
Nhóm UX viết lại error state theo nguyên tắc "recognize, diagnose, recover". Thông báo mới: "Ngân hàng đã từ chối giao dịch này. Tiền trong tài khoản của bạn chưa bị trừ. Bạn có thể thử lại bằng thẻ khác, hoặc chọn thanh toán khi nhận hàng (COD)." Kèm theo là hai nút rõ ràng: "Đổi phương thức thanh toán" và "Thử lại". Sau thay đổi, tỷ lệ người dùng hoàn tất đơn hàng sau khi gặp lỗi thanh toán tăng rõ rệt, và số cuộc gọi hỏi "tôi có bị trừ tiền không" giảm mạnh.
Bài học: Error state tốt phải trấn an (tiền chưa bị trừ), giải thích bằng ngôn ngữ đời thường và cung cấp lối thoát cụ thể. Đúng lúc người dùng lo lắng nhất chính là lúc thiết kế lỗi tạo ra hoặc phá hủy niềm tin — và ảnh hưởng trực tiếp đến doanh thu.
Ví dụ 3: Ứng dụng giao đồ ăn và edge case tên món quá dài
Một startup giao đồ ăn ở Đông Nam Á thiết kế thẻ (card) hiển thị món ăn rất đẹp với dữ liệu mẫu tiếng Anh ngắn gọn như "Beef Pho". Khi ra thị trường Việt Nam, các nhà hàng nhập tên món thật kiểu "Bún bò Huế đặc biệt tô lớn thêm giò heo và chả cua". Tên tràn ra khỏi thẻ, đẩy giá tiền và nút "Thêm" xuống lệch hàng, làm vỡ toàn bộ lưới hiển thị trên màn hình nhỏ. Trên một số máy Android màn hình hẹp, người dùng thậm chí không nhìn thấy nút đặt món.
Đội ngũ xử lý edge case này bằng cách: giới hạn tên món hiển thị tối đa 2 dòng với dấu "..." (text truncation), cố định vị trí giá và nút bất kể độ dài tên, đồng thời kiểm thử với cả tên rất ngắn ("Phở") lẫn tên rất dài để đảm bảo layout không vỡ ở cả hai cực. Họ cũng bổ sung trạng thái khi nhà hàng chưa tải ảnh món — hiển thị ảnh placeholder có logo thay vì ô xám trống.
Bài học: Dữ liệu thật luôn "bẩn" và cực đoan hơn dữ liệu mẫu. Edge case về độ dài văn bản và nội dung do người dùng tạo là nguyên nhân phổ biến nhất gây vỡ giao diện — đặc biệt với tiếng Việt có dấu và tên gọi dài. Luôn kiểm thử ở cả hai cực: quá ngắn và quá dài.
Hướng dẫn từng bước
Đây là quy trình để xử lý ba nhóm trạng thái một cách có hệ thống cho bất kỳ màn hình nào:
Bước 1 — Liệt kê mọi trạng thái của màn hình. Trước khi thiết kế, hãy viết ra danh sách: ideal, empty (và phân loại first-use / cleared / no-results), loading, error (validation và system), partial data. Dán checklist này ngay cạnh mỗi màn hình trong Figma.
Bước 2 — Với empty state, xác định loại và mục tiêu. Nếu là first-use, mục tiêu là dẫn dắt và tạo hành động đầu tiên. Viết nội dung trả lời ba câu hỏi: chức năng để làm gì, làm gì tiếp theo, bắt đầu nhanh thế nào. Cân nhắc thêm option dữ liệu mẫu hoặc template. Nếu là no-results, gợi ý người dùng bỏ bớt bộ lọc hoặc kiểm tra chính tả.
Bước 3 — Với error state, viết nội dung theo công thức "cái gì + tại sao + làm gì". Loại bỏ mọi mã lỗi kỹ thuật. Đặt lỗi validation ngay cạnh trường liên quan (inline). Với lỗi hệ thống, thêm câu trấn an dữ liệu an toàn và nút "Thử lại". Dùng giọng văn không đổ lỗi.
Bước 4 — Với edge case, kiểm thử ở các cực. Chuẩn bị bộ dữ liệu thử gồm: chuỗi rỗng, chuỗi cực dài, ký tự đặc biệt/emoji, tiếng Việt có dấu, số 0, số 1, số cực lớn (999+), không có ảnh. Chạy qua từng màn hình. Áp dụng truncation, cố định layout, dùng placeholder khi thiếu ảnh.
Bước 5 — Thiết kế loading state để giảm cảm giác chờ. Với thao tác mất hơn 1 giây, hiển thị skeleton screen (khung xương) thay vì spinner quay đơn thuần, giúp người dùng cảm nhận nội dung sắp hiện ra.
Bước 6 — Chốt bằng review và văn bản. Đưa nội dung (microcopy) của tất cả trạng thái cho người viết UX writing rà soát để đảm bảo giọng điệu nhất quán, và mời QA test đúng các kịch bản không-happy-path.
Lỗi thường gặp & mẹo
Lỗi 1 — Chỉ thiết kế happy path. Đây là lỗi phổ biến nhất. Mẹo: thêm cột "States" vào checklist thiết kế của mỗi màn hình và coi màn hình chưa hoàn thành nếu chưa có đủ empty/error/loading.
Lỗi 2 — Empty state chỉ ghi "Không có dữ liệu". Đây là màn hình lãng phí. Mẹo: luôn kèm CTA và, nếu có thể, dữ liệu mẫu/template để người dùng thấy giá trị ngay.
Lỗi 3 — Thông báo lỗi bằng ngôn ngữ máy. "Error 404", "undefined" hiện ra trước mặt người dùng cuối là dấu hiệu quên xử lý. Mẹo: mọi mã lỗi kỹ thuật phải được "dịch" sang câu người dùng hiểu; giữ log kỹ thuật ở phía dev.
Lỗi 4 — Đổ lỗi cho người dùng. Câu "Bạn nhập sai" gây khó chịu. Mẹo: chuyển sang mô tả yêu cầu ("Mật khẩu cần ít nhất 8 ký tự") thay vì phán xét.
Lỗi 5 — Chỉ test với dữ liệu mẫu đẹp. Mẹo: giữ sẵn một bộ dữ liệu "xấu" (dài, rỗng, đặc biệt) và luôn test với nó.
Lỗi 6 — Quên trạng thái no-results khác first-use. Người tìm kiếm không ra kết quả cần gợi ý khác với người dùng lần đầu. Đừng dùng chung một màn hình cho cả hai.
Mẹo nâng cao: Empty state và error state là cơ hội thể hiện tính cách thương hiệu (brand personality) — một minh họa dễ thương, một câu chữ có duyên đúng lúc người dùng bối rối có thể biến khoảnh khắc tiêu cực thành điểm cộng đáng nhớ. Nhưng đừng lạm dụng sự hài hước ở màn hình lỗi nghiêm trọng như mất tiền hay mất dữ liệu.
Bài tập thực hành
- Kiểm kê trạng thái: Chọn một sản phẩm bạn đang dùng (app ngân hàng, ví điện tử, app đặt xe). Chọn một màn hình bất kỳ và liệt kê ra tối thiểu 5 trạng thái của nó (ideal, empty, error, loading, partial). Ghi chú xem sản phẩm đã xử lý trạng thái nào tốt, trạng thái nào bị bỏ quên.
- Thiết kế lại một empty state: Tìm một first-use empty state kém (chỉ ghi "chưa có dữ liệu"). Viết lại nội dung theo ba câu hỏi (để làm gì / làm gì tiếp / bắt đầu nhanh thế nào) và đề xuất một tùy chọn dữ liệu mẫu hoặc template. Phác thảo trên giấy hoặc Figma.
- Viết lại 3 thông báo lỗi: Lấy 3 error message kỹ thuật (ví dụ "Error 500", "Invalid input", "Timeout") và viết lại mỗi cái theo công thức "cái gì đã xảy ra + tại sao + làm gì tiếp theo", bằng giọng văn không đổ lỗi và có nút hành động.
- Săn edge case: Nhập vào một form/thẻ hiển thị các dữ liệu cực đoan: tên tiếng Việt rất dài, ô để trống, emoji, số 999999. Chụp lại chỗ giao diện bị vỡ và đề xuất cách khắc phục (truncation, cố định layout, placeholder).
Tóm tắt
Empty states, error states và edge cases là những phần của trải nghiệm mà người dùng thật sự gặp phải rất thường xuyên, nhưng lại hay bị bỏ quên vì chúng ta chỉ chăm chút happy path. Ba ý cốt lõi cần nhớ:
- Empty state — đặc biệt là first-use — phải trả lời ba câu hỏi: chức năng để làm gì, làm gì tiếp theo (CTA rõ ràng), và cách bắt đầu nhanh (dữ liệu mẫu / template). Phân biệt rõ first-use, user-cleared và no-results vì cách xử lý khác nhau.
- Error state phải giúp người dùng nhận ra, chẩn đoán và khắc phục lỗi: nói bằng ngôn ngữ con người, không đổ lỗi, trấn an khi cần, và luôn có lối thoát cụ thể. Đây là điểm chạm quyết định niềm tin và doanh thu.
- Edge case là nơi giao diện dễ vỡ nhất. Dữ liệu thật luôn cực đoan hơn dữ liệu mẫu — hãy luôn test ở cả hai cực (quá ngắn, quá dài, rỗng, đặc biệt), đặc biệt với tiếng Việt có dấu và nội dung do người dùng tạo.