# Don't Pre-disable Submit

**MUST** · **ID:** `forms-dont-pre-disable-submit` · **Category:** forms
**Source:** [Vercel](https://github.com/vercel-labs/agent-skills/blob/main/skills/web-design-guidelines/SKILL.md)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/forms-dont-pre-disable-submit

> MUST: Allow submitting incomplete forms to surface validation

Allow submitting incomplete forms to surface validation feedback

When the submit button is disabled before the user has tried to submit, they can't discover what's wrong with their form. Enable the button, let them click it, then show comprehensive validation feedback that guides them to fix all issues.

## Bad — do not do this

`forms-dont-pre-disable-submit-bad`

```tsx
import { useState } from 'react';

export function DontPreDisableSubmitBad() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const isValid = email.includes('@') && password.length >= 8;

  return (
    <div className="w-full max-w-sm">
      <form className="space-y-4">
        <div>
          <label htmlFor="bad-disable-email" className="block text-sm font-medium text-foreground mb-1">
            Email
          </label>
          <input
            id="bad-disable-email"
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            className="w-full px-3 py-2 border border-border rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
            placeholder="you@example.com"
          />
        </div>
        <div>
          <label htmlFor="bad-disable-password" className="block text-sm font-medium text-foreground mb-1">
            Password
          </label>
          <input
            id="bad-disable-password"
            type="password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            className="w-full px-3 py-2 border border-border rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
            placeholder="Min 8 characters"
          />
        </div>
        <button
          type="submit"
          disabled={!isValid}
          className="w-full px-4 py-2 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors disabled:bg-muted disabled:cursor-not-allowed"
        >
          Sign Up
        </button>
      </form>
      <p className="text-xs text-error mt-4">
        Submit button is disabled until valid. Users can't discover what's wrong.
      </p>
    </div>
  );
}
```

## Good — do this

`forms-dont-pre-disable-submit-good`

```tsx
import { useState } from 'react';

export function DontPreDisableSubmitGood() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [errors, setErrors] = useState<{ email?: string; password?: string }>({});
  const [touched, setTouched] = useState(false);

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    setTouched(true);

    const newErrors: { email?: string; password?: string } = {};
    if (!email.includes('@')) {
      newErrors.email = 'Please enter a valid email address';
    }
    if (password.length < 8) {
      newErrors.password = 'Password must be at least 8 characters';
    }

    setErrors(newErrors);

    if (Object.keys(newErrors).length === 0) {
      alert('Form submitted successfully!');
    }
  };

  return (
    <div className="w-full max-w-sm">
      <form onSubmit={handleSubmit} className="space-y-4">
        <div>
          <label htmlFor="good-disable-email" className="block text-sm font-medium text-foreground mb-1">
            Email
          </label>
          <input
            id="good-disable-email"
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            className="w-full px-3 py-2 border border-border rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
            placeholder="you@example.com"
          />
          {touched && errors.email && (
            <p className="text-xs text-error mt-1">{errors.email}</p>
          )}
        </div>
        <div>
          <label htmlFor="good-disable-password" className="block text-sm font-medium text-foreground mb-1">
            Password
          </label>
          <input
            id="good-disable-password"
            type="password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            className="w-full px-3 py-2 border border-border rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
            placeholder="Min 8 characters"
          />
          {touched && errors.password && (
            <p className="text-xs text-error mt-1">{errors.password}</p>
          )}
        </div>
        <button
          type="submit"
          className="w-full px-4 py-2 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors"
        >
          Sign Up
        </button>
      </form>
      <p className="text-xs text-success mt-4">
        Submit is always enabled. Clicking shows validation feedback.
      </p>
    </div>
  );
}
```

## References

- [Form validation UX](https://www.smashingmagazine.com/2022/09/inline-validation-web-forms-ux/)
