Volver al blog

Diseño

UX en Productos SaaS: Diseñar para Retener

7 min lecturaEnviaIT Engineering

Hay una diferencia brutal entre un SaaS que la gente usa y uno que la gente quiere usar. Los dos pueden resolver el mismo problema, pero el segundo tiene un churn del 3% mensual y el primero del 12%. La diferencia casi nunca es funcionalidad — es experiencia.

En EnviaIT diseñamos y construimos productos SaaS para clientes que necesitan retener usuarios en mercados competitivos. Después de varios proyectos (incluido Nudato, nuestra plataforma de gestión de eventos), hemos destilado los principios que realmente mueven métricas.

Onboarding: los primeros 5 minutos definen todo

El 40-60% de los usuarios que se registran en un SaaS nunca vuelven después de la primera sesión. Eso no es un problema de marketing — es un problema de diseño.

Un buen onboarding no es un tour de 15 pasos con tooltips. Es llevar al usuario a su primer momento de valor lo más rápido posible.

El patrón que funciona

Registro → Pregunta contextual (1-2) → Setup mínimo → Valor inmediato

En Nudato, reducimos el onboarding de organizadores de eventos de 8 pasos a 3:

  1. Nombre del evento y fecha — lo mínimo para crear algo
  2. Importar agenda (CSV o manual) — el usuario ve su evento tomando forma
  3. Invitar al primer asistente — momento de valor: "esto funciona"

Todo lo demás (branding, configuración de notificaciones, integraciones) se presenta después, cuando el usuario ya ha invertido emocionalmente en el producto.

La regla es simple: si puedes posponer un paso sin romper la experiencia core, posponlo. Cada campo extra en el onboarding es una oportunidad para que el usuario cierre la pestaña.

Indicadores de progreso que motivan

Los checklists de onboarding funcionan cuando están bien diseñados:

| Elemento | Buena práctica | Error común | |----------|---------------|-------------| | Progreso visual | Barra que empieza en 20% (nunca en 0%) | Mostrar 0/10 completado | | Tareas | 3-5 acciones concretas | Lista de 12 configuraciones | | Recompensa | Confetti o badge al completar | Nada — el checklist simplemente desaparece | | Persistencia | Visible pero no bloqueante | Modal que impide usar el producto |

Arquitectura de información: dónde poner cada cosa

La navegación de un SaaS es su esqueleto. Si está mal estructurada, da igual lo bonitas que sean las pantallas — el usuario se pierde.

El modelo mental del usuario, no el tuyo

El error más común es organizar la navegación según la estructura del código o la base de datos. El usuario no piensa en "entidades" — piensa en tareas.

❌ Navegación por entidades:
   Dashboard | Eventos | Asistentes | Salas | Speakers | Configuración

✅ Navegación por flujo de trabajo:
   Mi Evento | Agenda | Personas | Comunicación | Configuración

En el segundo caso, "Personas" agrupa asistentes, speakers e invitados — porque desde la perspectiva del organizador, todas son "personas en mi evento". La estructura técnica es irrelevante para la UX.

Profundidad vs amplitud

Para SaaS con muchas funcionalidades, aplica la regla de 7±2: no más de 7 elementos en el primer nivel de navegación. Usa agrupación y navegación secundaria para el resto.

// Navegación principal: máximo 7 items
const mainNav = [
  { label: 'Dashboard', icon: HomeIcon },
  { label: 'Proyectos', icon: FolderIcon },
  { label: 'Equipo', icon: UsersIcon },
  { label: 'Informes', icon: ChartIcon },
  { label: 'Configuración', icon: GearIcon },
];

// Navegación secundaria dentro de cada sección
const projectSubNav = [
  'Resumen', 'Tareas', 'Archivos', 'Integraciones'
];

Progressive disclosure: mostrar solo lo necesario

Este principio es fundamental y sin embargo se viola constantemente. Progressive disclosure significa mostrar información y opciones solo cuando el usuario las necesita.

Niveles de complejidad

Diseñamos cada pantalla con tres niveles:

  1. Vista rápida — La información esencial que el 80% de los usuarios necesita el 80% del tiempo
  2. Vista detallada — Datos adicionales accesibles con un clic
  3. Vista avanzada — Configuración experta, accesible pero nunca prominente
// Ejemplo: formulario de crear evento
function CreateEventForm() {
  const [showAdvanced, setShowAdvanced] = useState(false);

  return (
    <form>
      {/* Nivel 1: siempre visible */}
      <Input label="Nombre del evento" required />
      <DatePicker label="Fecha" required />
      <Select label="Tipo" options={eventTypes} />

      {/* Nivel 2: toggle explícito */}
      <Collapsible
        trigger="Opciones avanzadas"
        open={showAdvanced}
        onToggle={setShowAdvanced}
      >
        <Input label="Capacidad máxima" />
        <Select label="Zona horaria" />
        <Toggle label="Requiere aprobación" />
        <Input label="URL personalizada" />
      </Collapsible>
    </form>
  );
}

El resultado: la mayoría de usuarios crean un evento en 30 segundos. Los power users tienen acceso a todo sin que la interfaz se sienta abrumadora.

Estados que nadie diseña (y todos ven)

Los diseñadores suelen enfocarse en el happy path: la pantalla con datos, el formulario completado, la lista llena. Pero los usuarios pasan una cantidad sorprendente de tiempo en estados que nadie diseñó.

Loading states

Un spinner genérico dice "espera". Un skeleton screen dice "ya casi está tu contenido". La diferencia es percepción de velocidad.

// ❌ Spinner genérico
function EventList() {
  if (loading) return <Spinner />;
  return <List items={events} />;
}

// ✅ Skeleton que anticipa el contenido
function EventList() {
  if (loading) {
    return (
      <div className="space-y-4">
        {[1, 2, 3].map((i) => (
          <div key={i} className="animate-pulse">
            <div className="h-6 bg-gray-200 rounded w-3/4 mb-2" />
            <div className="h-4 bg-gray-100 rounded w-1/2" />
          </div>
        ))}
      </div>
    );
  }
  return <List items={events} />;
}

Empty states

Un empty state es una oportunidad de activación, no un callejón sin salida.

❌ "No hay eventos"
✅ "Aún no tienes eventos. Crea tu primer evento en menos de un minuto."
   [Crear evento →]

En Nudato, los empty states incluyen:

  • Título explicativo que no culpa al usuario
  • Ilustración contextual (no genérica)
  • CTA primario que inicia la acción más obvia
  • Enlace secundario a documentación si el concepto es nuevo

Error handling UX

Los errores son inevitables. Lo que importa es cómo los comunicas.

Principios que seguimos:

  1. Lenguaje humano, no códigos de error. "No pudimos guardar los cambios. Revisa tu conexión e inténtalo de nuevo" en lugar de "Error 500: Internal Server Error"
  2. Contexto específico. Si un campo es inválido, señala exactamente cuál y por qué
  3. Acción clara. Cada mensaje de error incluye qué puede hacer el usuario para resolverlo
  4. Recuperación sin pérdida de datos. Si un formulario falla al enviar, los datos deben seguir ahí cuando se reintente
// Patrón de error con recuperación
function SaveButton({ onSave, formData }) {
  const [error, setError] = useState(null);
  const [retrying, setRetrying] = useState(false);

  const handleSave = async () => {
    try {
      setError(null);
      await onSave(formData);
    } catch (err) {
      setError({
        message: 'No pudimos guardar los cambios.',
        action: 'Revisa tu conexión e inténtalo de nuevo.',
        canRetry: true,
      });
    }
  };

  return (
    <>
      <Button onClick={handleSave} loading={retrying}>
        Guardar
      </Button>
      {error && (
        <ErrorBanner
          message={error.message}
          action={error.action}
          onRetry={error.canRetry ? handleSave : undefined}
        />
      )}
    </>
  );
}

Accesibilidad: no es opcional

La accesibilidad no es un nice-to-have ni un requisito legal que cumplir a regañadientes. Es buen diseño. Un producto accesible es un producto más usable para todos.

Lo mínimo que implementamos en cada proyecto

  • Navegación por teclado completa. Cada acción alcanzable con Tab, Enter y Escape
  • Contraste de color WCAG AA. Ratio mínimo de 4.5:1 para texto normal, 3:1 para texto grande
  • Labels en formularios. Siempre <label> asociado al input, nunca solo placeholder
  • Roles ARIA donde el HTML semántico no es suficiente
  • Focus visible. Los outlines de focus no se eliminan — se diseñan bonitos
// Un botón accesible no requiere esfuerzo extra —
// requiere no eliminar lo que el navegador ya da
<button
  onClick={handleAction}
  aria-label="Eliminar evento Conferencia Tech 2025"
  className="focus:outline-none focus:ring-2 focus:ring-blue-500
             focus:ring-offset-2 rounded-md"
>
  <TrashIcon aria-hidden="true" />
</button>

Testing de accesibilidad

Usamos una combinación de herramientas automatizadas y testing manual:

  • axe-core integrado en los tests para detectar violaciones WCAG automáticamente
  • Lighthouse en CI para medir la puntuación de accesibilidad en cada deploy
  • Testing manual con lector de pantalla (VoiceOver) en las pantallas principales antes de cada release

Design system: consistencia a escala

Cuando un SaaS crece, la inconsistencia visual se convierte en deuda de diseño. Un botón tiene 4 variantes diferentes en 4 pantallas. Los espaciados no siguen ningún patrón. Los colores se definen inline.

Nuestro approach con design tokens

Todo empieza con tokens — valores atómicos que definen el lenguaje visual:

/* tokens.css */
:root {
  /* Colores semánticos, no visuales */
  --color-primary: #2563eb;
  --color-primary-hover: #1d4ed8;
  --color-danger: #dc2626;
  --color-success: #16a34a;
  --color-text-primary: #1e293b;
  --color-text-secondary: #64748b;
  --color-bg-primary: #ffffff;
  --color-bg-secondary: #f8fafc;

  /* Espaciado con escala consistente */
  --space-xs: 0.25rem;   /* 4px */
  --space-sm: 0.5rem;    /* 8px */
  --space-md: 1rem;      /* 16px */
  --space-lg: 1.5rem;    /* 24px */
  --space-xl: 2rem;      /* 32px */
  --space-2xl: 3rem;     /* 48px */

  /* Tipografía */
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --font-size-xl: 1.25rem;
  --font-size-2xl: 1.5rem;
}

Los componentes se construyen sobre estos tokens, nunca con valores hardcodeados. El resultado: cambiar el color primario en toda la aplicación es editar una sola línea.

Métricas que importan

Diseñar sin medir es decorar. Las dos métricas que más correlacionan con la calidad de UX en un SaaS son activación y retención.

Tasa de activación

Definición: porcentaje de usuarios que completan la acción clave dentro de los primeros X días.

En Nudato, la acción clave es "crear un evento con al menos 1 sesión en la agenda". Medimos:

  • Activación día 1: 34% → 52% (después de rediseñar el onboarding)
  • Activación día 7: 41% → 63%

Retención

Definición: porcentaje de usuarios que vuelven al producto en un periodo determinado.

| Periodo | Antes del rediseño | Después | |---------|-------------------|---------| | Día 1 | 28% | 45% | | Día 7 | 18% | 31% | | Día 30 | 9% | 22% |

Ese salto del 9% al 22% en retención a 30 días representó un impacto directo en MRR. No añadimos ninguna feature nueva — solo mejoramos cómo se presentaban las existentes.

Cómo medimos

Usamos una combinación de:

  • Mixpanel para funnel de activación y eventos de usuario
  • Hotjar para grabaciones de sesión y heatmaps en pantallas clave
  • Encuestas in-app (NPS y CSAT) con frecuencia trimestral
  • Entrevistas cualitativas mensuales con 3-5 usuarios activos

Los datos cuantitativos te dicen qué está pasando. Las entrevistas te dicen por qué. Necesitas ambos.

El principio que lo une todo

Si tuviéramos que reducir nuestra filosofía de UX a una sola frase sería esta: reduce la fricción entre la intención del usuario y el resultado. Cada clic extra, cada campo innecesario, cada pantalla confusa es fricción. Y la fricción mata retención.

No se trata de hacer las cosas bonitas (aunque ayuda). Se trata de hacer las cosas obvias. Cuando un usuario puede completar su tarea sin pensar en la herramienta, has hecho bien tu trabajo.


¿Tu producto SaaS tiene problemas de retención? Hablemos — te ayudamos a identificar las fricciones y diseñar soluciones que muevan métricas.