Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Bài 46 — Error States & Form Validation UI

Web Design Basics Bài 46/60

Mở đầu — vì sao bài này quan trọng

Hãy hình dung bạn vừa điền xong một form đăng ký dài 12 trường thông tin trên một website của ngân hàng. Bạn bấm nút "Hoàn tất", trang load lại, và một dòng chữ đỏ duy nhất hiện lên trên cùng: "Đã có lỗi xảy ra. Vui lòng kiểm tra lại." Không nói rõ trường nào sai, không giữ lại dữ liệu bạn đã gõ, và bạn phải gõ lại từ đầu. Cảm giác đó — bực bội, bất lực, muốn bỏ cuộc — chính là thứ mà một designer giỏi phải có trách nhiệm tiêu diệt.

Error states (trạng thái lỗi) và form validation UI (giao diện xác thực biểu mẫu) là nơi mà thiết kế chạm trực tiếp đến cảm xúc người dùng tại đúng khoảnh khắc họ dễ bỏ cuộc nhất. Một nghiên cứu của Baymard Institute (chuyên về UX thương mại điện tử) cho thấy thiết kế form và xử lý lỗi kém là một trong những nguyên nhân hàng đầu khiến người dùng bỏ giỏ hàng ở bước thanh toán. Với một sàn như Tiki hay Shopee, mỗi phần trăm người dùng bỏ cuộc ở bước checkout có thể tương đương hàng tỷ đồng doanh thu mỗi tháng.

Điều quan trọng cần khắc cốt: lỗi không phải là lỗi của người dùng — đó là khoảng cách giữa kỳ vọng của họ và những gì hệ thống chấp nhận. Nhiệm vụ của error UI không phải để "trừng phạt" người dùng vì gõ sai, mà để dẫn họ quay lại con đường thành công nhanh nhất, ít đau đớn nhất. Bài này sẽ dạy bạn ba loại lỗi cốt lõi, khi nào dùng loại nào, và cách thiết kế từng loại sao cho người dùng cảm thấy được giúp đỡ chứ không bị mắng.

Khái niệm cốt lõi

Trước khi đi vào chi tiết, cần phân biệt rõ: chúng ta đang nói về cách hiển thị lỗi cho người dùng, không phải logic kiểm tra dữ liệu ở backend. Cùng một lỗi "email sai định dạng" có thể được trình bày theo nhiều cách khác nhau, và sự khác biệt đó quyết định trải nghiệm.

Có ba kênh hiển thị lỗi chính, và mỗi kênh phục vụ một loại tình huống riêng.

Inline errors — lỗi cấp trường (field-level)

Đây là loại lỗi gắn trực tiếp vào từng ô nhập liệu cụ thể. Khi người dùng gõ email "nguyenvan@" thiếu phần đuôi, dòng chữ "Email chưa đúng định dạng, ví dụ: ten@gmail.com" hiện ngay bên dưới ô email đó, kèm viền ô chuyển sang màu đỏ và một icon cảnh báo.

Inline error là loại quan trọng nhất và nên là lựa chọn mặc định cho phần lớn lỗi xác thực dữ liệu nhập. Lý do: nó chỉ thẳng vào đúng chỗ sai, người dùng không phải đoán, và sửa được ngay tại chỗ. Nguyên tắc vàng là đặt thông báo lỗi càng gần điểm gây lỗi càng tốt — về cả không gian (ngay dưới trường) lẫn thời gian (ngay khi phát hiện được).

Một câu hỏi kinh điển: validate khi nào? Có ba thời điểm:

  • On submit (khi bấm gửi): an toàn nhất nhưng phản hồi chậm, người dùng đã đi hết form mới biết sai.
  • On blur (khi rời khỏi trường): cân bằng tốt nhất. Người dùng gõ xong email, chuyển sang trường khác, lúc đó mới kiểm tra. Không làm phiền giữa lúc đang gõ.
  • On change (khi đang gõ từng ký tự): dễ gây khó chịu nếu báo lỗi quá sớm — vừa gõ chữ "n" đã báo "email sai". Tuy nhiên rất hữu ích cho phản hồi tích cực như thanh đo độ mạnh mật khẩu, hoặc để xoá lỗi ngay khi người dùng bắt đầu sửa.
Pattern được khuyến nghị rộng rãi: validate on blur lần đầu, sau đó validate on change cho riêng trường đã từng báo lỗi. Nghĩa là đừng la mắng khi họ chưa gõ xong, nhưng một khi đã chỉ ra lỗi, hãy gỡ thông báo lỗi ngay khoảnh khắc họ sửa đúng — đó là cảm giác "được giúp đỡ".

Form-level errors — lỗi cấp toàn form

Loại này hiển thị ở đầu form (hoặc cuối form, gần nút submit), dùng cho những lỗi không thuộc về một trường cụ thể, hoặc lỗi đến từ phía server sau khi đã gửi.

Ví dụ điển hình: người dùng nhập đúng định dạng email và mật khẩu, nhưng server trả về "Email hoặc mật khẩu không đúng". Đây không phải lỗi định dạng của một trường — nó là lỗi của tổ hợp, hoặc lỗi nghiệp vụ. Một banner đỏ ở đầu form là nơi phù hợp.

Form-level error cũng đóng vai trò "bản tóm tắt" khi có nhiều lỗi cùng lúc: "Có 3 trường cần sửa", kèm danh sách link nhảy thẳng đến từng trường lỗi — cực kỳ quan trọng cho accessibility (người dùng screen reader cần biết tổng quan ngay).

Toasts — thông báo lỗi thoáng qua (transient)

Toast là dải thông báo nhỏ thường xuất hiện ở góc (top-right hoặc bottom), tự biến mất sau vài giây. Loại này hợp với lỗi không gắn với việc sửa trường nào cả, đặc biệt là lỗi hệ thống xảy ra sau một hành động: "Mất kết nối mạng, không lưu được thay đổi", "Tải file thất bại".

Cảnh báo lớn: đừng bao giờ dùng toast để báo lỗi validation của form. Toast tự biến mất, người dùng có thể chưa kịp đọc, và nó không neo vào trường sai. Một lỗi mà người dùng cần hành động để sửa thì phải dùng inline hoặc form-level — thứ tồn tại bền vững trên màn hình cho đến khi được giải quyết.

Bốn yếu tố tạo nên một thông báo lỗi tốt

Bất kể kênh nào, một thông báo lỗi chất lượng cần: (1) chỉ rõ chỗ nào sai, (2) giải thích vì sao sai bằng ngôn ngữ con người, (3) gợi ý cách sửa, và (4) giữ giọng điệu trung tính, không đổ lỗi. So sánh: "Lỗi nhập liệu" (tệ) với "Số điện thoại cần đủ 10 chữ số, bạn đang nhập 9 số" (tốt — vừa chỉ chỗ, vừa nói lý do, vừa ngầm gợi ý cách sửa).

Tình huống thực tế

Ví dụ 1 — Form đăng ký của một sàn TMĐT Việt Nam reload mất sạch dữ liệu

Một startup TMĐT tại TP.HCM (gọi là "ChợViệt") có form đăng ký tài khoản 8 trường. Phiên bản đầu tiên validate hoàn toàn ở server: người dùng bấm "Đăng ký", trang reload, nếu sai thì hiện một dòng đỏ trên cùng "Thông tin không hợp lệ" và xoá trắng toàn bộ các trường mật khẩu. Team phát hiện tỷ lệ hoàn tất đăng ký chỉ khoảng 41%.

Khi xem session recording, họ thấy người dùng gõ lại form đến lần thứ hai, lần thứ ba, rồi đóng tab. Vấn đề kép: lỗi không chỉ ra trường nào sai (thực ra chỉ là mật khẩu thiếu ký tự đặc biệt), và việc xoá trắng buộc gõ lại từ đầu.

Họ làm lại theo hướng inline: validate on blur, hiện thông báo cụ thể ngay dưới ô mật khẩu ("Mật khẩu cần ít nhất 1 ký tự đặc biệt như @ hoặc #"), giữ nguyên mọi dữ liệu đã nhập khi có lỗi, và thêm thanh đo độ mạnh mật khẩu cập nhật on-change. Tỷ lệ hoàn tất tăng lên khoảng 67% trong vòng một tháng.

Bài học: Không bao giờ xoá dữ liệu người dùng đã nhập khi báo lỗi (trừ trường mật khẩu vì lý do bảo mật — nhưng cũng nên cân nhắc và ít nhất giữ lại các trường khác). Và lỗi mơ hồ ở cấp form là kẻ giết chết tỷ lệ chuyển đổi.

Ví dụ 2 — Ngân hàng số dùng toast cho lỗi chuyển tiền

Một ứng dụng ngân hàng số (giả định "TPay") báo lỗi giao dịch chuyển tiền bằng toast ở góc trên: khi số tài khoản người nhận sai, một toast đỏ trượt ra rồi biến mất sau 3 giây. Nhiều người dùng lớn tuổi không kịp đọc, cứ bấm "Chuyển" liên tục và nhận hết toast này đến toast khác, không hiểu vì sao không chuyển được.

Đội thiết kế nhận ra mình đã dùng sai kênh: lỗi "số tài khoản không tồn tại" là lỗi cần người dùng sửa một trường cụ thể, nên phải là inline error neo dưới ô số tài khoản, kèm trạng thái nút "Chuyển" bị disable cho đến khi sửa. Toast chỉ nên dành cho thông báo "Chuyển tiền thành công" hoặc lỗi hệ thống như mất mạng. Sau khi đổi sang inline với thông báo "Số tài khoản này không tồn tại tại Vietcombank, vui lòng kiểm tra lại", số lần bấm lặp giảm rõ rệt và lượt gọi tổng đài hỗ trợ về chủ đề này giảm hẳn.

Bài học: Lỗi cần hành động sửa thì phải tồn tại bền vững trên màn hình. Toast thoáng qua chỉ hợp với thông tin không bắt buộc phải hành động ngay.

Ví dụ 3 — Grab và việc xử lý lỗi mạng khi đặt xe

Ở Đông Nam Á, kết nối mạng chập chờn là chuyện thường. Khi người dùng đặt xe mà request thất bại vì timeout, một thiết kế tệ sẽ hiện "Error 500" hoặc đơn giản là không phản hồi gì cả — người dùng không biết đã đặt được chưa, có dám bấm lại không vì sợ đặt nhầm hai chuyến.

Cách xử lý tốt: phân biệt rõ lỗi do người dùng (sửa được) và lỗi hệ thống (thử lại được). Với lỗi mạng, hiển thị thông báo trung tính "Kết nối không ổn định, chưa gửi được yêu cầu" kèm một nút "Thử lại" rõ ràng — biến trạng thái lỗi thành một hành động khôi phục, thay vì ngõ cụt. Quan trọng là đảm bảo idempotency ở backend để bấm lại không tạo hai đơn, và phần UI phải truyền đạt sự an toàn đó.

Bài học: Mọi error state nên cố gắng cung cấp một "lối ra" — nút thử lại, link liên hệ, hoặc gợi ý hành động — thay vì để người dùng mắc kẹt.

Hướng dẫn từng bước

Đây là quy trình thực hành khi bạn thiết kế error UI cho một form bất kỳ.

Bước 1 — Liệt kê mọi trạng thái lỗi có thể xảy ra cho từng trường. Với ô email: để trống, sai định dạng, email đã tồn tại. Với mật khẩu: quá ngắn, thiếu ký tự, không khớp xác nhận. Viết ra hết. Đừng chỉ thiết kế trạng thái "đẹp" mặc định rồi quên rằng phần lớn thời gian người dùng sẽ gặp các trạng thái biên này.

Bước 2 — Phân loại mỗi lỗi vào đúng kênh. Lỗi định dạng một trường → inline. Lỗi tổ hợp/nghiệp vụ/server → form-level banner. Lỗi hệ thống không gắn trường → toast. Vẽ ra bảng phân loại này trước khi thiết kế giao diện.

Bước 3 — Chọn thời điểm validate. Mặc định: on blur lần đầu, on change sau khi đã có lỗi. Riêng các trường định dạng phức tạp (mật khẩu) có thể thêm phản hồi tích cực on-change.

Bước 4 — Viết nội dung thông báo theo công thức "chỗ nào + vì sao + cách sửa". Tránh thuật ngữ kỹ thuật. "Invalid input" → "Ngày sinh cần theo định dạng ngày/tháng/năm". Viết bằng tiếng Việt tự nhiên như đang nói chuyện với người dùng.

Bước 5 — Thiết kế phần nhìn của trạng thái lỗi. Đừng chỉ dựa vào màu đỏ (người mù màu sẽ không thấy — đây là điểm nối với bài Color Accessibility). Kết hợp: viền đỏ + icon cảnh báo + text mô tả. Ba tín hiệu này dư thừa một cách có chủ đích để ai cũng nhận ra.

Bước 6 — Xử lý hành vi submit. Khi bấm gửi mà còn lỗi: cuộn (scroll) màn hình đến trường lỗi đầu tiên, đặt focus vào đó, hiển thị bản tóm tắt nếu nhiều lỗi. Tuyệt đối không xoá dữ liệu đã nhập.

Bước 7 — Thiết kế trạng thái khôi phục. Lỗi được sửa thì thông báo phải biến mất ngay. Lỗi hệ thống thì kèm nút thử lại. Luôn có một lối ra.

Lỗi thường gặp & mẹo

Chỉ dùng màu để báo lỗi. Viền đỏ đơn thuần không đủ cho người mù màu (khoảng 8% nam giới). Luôn kết hợp icon và text.

Báo lỗi quá sớm khi đang gõ. Validate on-change ngay từ ký tự đầu khiến người dùng bị "mắng" trước khi kịp gõ xong. Hãy đợi on-blur cho lần đầu.

Thông báo lỗi mơ hồ. "Đã có lỗi xảy ra" là câu vô dụng nhất trong thiết kế. Luôn cụ thể.

Dùng toast cho lỗi cần sửa. Toast biến mất, người dùng đọc hụt, và không neo vào trường. Sai kênh nghiêm trọng.

Đổ lỗi cho người dùng. Tránh "Bạn đã nhập sai". Dùng giọng trung tính: "Số điện thoại cần đủ 10 chữ số".

Disable nút submit một cách im lặng. Nếu nút bị mờ mà không nói vì sao, người dùng bối rối không biết phải làm gì. Hoặc giải thích lý do disable, hoặc cho phép bấm rồi hiện lỗi inline.

Mẹo: Hãy thiết kế thông báo "thành công" tương xứng với lỗi. Khi người dùng sửa đúng, một dấu tích xanh nhỏ bên cạnh trường tạo cảm giác an tâm, khích lệ họ đi tiếp.

Mẹo: Với lỗi server, hãy có sẵn một thông báo dự phòng thân thiện cho trường hợp 500 không lường trước, thay vì để lộ stack trace kỹ thuật.

Bài tập thực hành

  • Audit một form thật. Mở form đăng ký của một app bạn hay dùng (Shopee, Momo, hoặc bất kỳ). Cố tình nhập sai từng trường và chụp lại cách nó báo lỗi. Phân loại mỗi lỗi vào inline / form-level / toast. Đánh giá: thông báo có cụ thể không? Có chỉ chỗ + vì sao + cách sửa không?
  • Thiết kế lại trong Figma. Lấy một form đăng ký 5 trường (họ tên, email, số điện thoại, mật khẩu, xác nhận mật khẩu). Vẽ đủ các trạng thái: mặc định, focus, lỗi (kèm icon + text), và thành công. Viết nội dung thông báo lỗi tiếng Việt cho từng trường theo công thức ba phần.
  • Viết bảng phân loại lỗi. Cho form thanh toán đơn hàng, liệt kê ít nhất 8 loại lỗi có thể xảy ra và gán mỗi loại vào đúng kênh hiển thị, kèm lý do.

Tóm tắt

Error states và form validation UI là điểm chạm quyết định giữa người dùng hoàn tất hay bỏ cuộc. Ba kênh cốt lõi: inline cho lỗi cấp trường (mặc định, neo gần điểm sai), form-level cho lỗi tổ hợp và lỗi server, toast cho lỗi hệ thống thoáng qua không cần sửa trường. Validate on-blur lần đầu rồi on-change sau đó. Mọi thông báo tốt đều chỉ rõ chỗ nào sai, vì sao sai và cách sửa, bằng giọng trung tính không đổ lỗi. Đừng bao giờ chỉ dựa vào màu, đừng dùng toast cho lỗi cần hành động, đừng xoá dữ liệu đã nhập, và luôn cung cấp một lối ra. Lỗi không phải để trừng phạt người dùng — nó là cơ hội để bạn, với tư cách designer, dẫn họ về lại con đường thành công một cách nhẹ nhàng nhất.