Example: Debounced Validation

Hi everyone.
I’m currently working on an open source collection of useful code snippets and good practices, over at Orasund/elm-cookbook.

I recently got asked to add an example for how to debounce a form validation:
The validation should only fire if the user has not typed for 500ms.

I’m definitely no expert in that field and I don’t want to reinvent the wheel. That’s why I’m asking for feedback.

After looking at a two examples (a gist by jcaxmacher and the elm seed example), I came up with the following Ellie:


Here are a few things to keep in mind:

  • I don’t want to include any custom packages. Packages that would do the job are:
  • The example should stay slim and simple.
    • No over-abstraction like in the elm seed example.
    • I intentionally used subscriptions instead of Task.sleep, as I believe newcomers have a better understanding as to what a subscription does opposed to what a task does.

If you have any additional insight or useful resources please tell me. Also, I will link this topic in my elm-cookbook; So here’s a quick :wave: to all my future readers.


I had to implement a debounce recently, and I wrote it exactly the way you did (except there was no ambiguity about which field needed validation, so it was just a countDown : Maybe Int).

The implementation you have has the potential to throw away input data. Basically, if the input in the second field happens within the debounce interval, the old input is lost as it is being replaced by the new input.

You can see this bug/issue if you either increase the msUntilTimout to something larger that would allow you to type in the name field, switch to the password field and type something (the data inputed in the Name field is lost).

Of course, if you type fast and use tab to switch between fields you can reproduce the bug even with 500 ms.

Other than that, the implementation is elegant. I like it. :slight_smile:


To avoid the problems mentioned by @pdamoc, on my current project I’m passing all the info into the messages. This allows each field to be debounced independently (and possibly in parallel).

type Msg
    = InputOccurred FieldName String
    | TimePassed FieldName String

It might be hard to vary the messages with a subscription though. I’m using a task on my project.

Heres a modification to your ellie to show the approach I’m using on my project.

1 Like

Blur is very useful for avoiding those race conditions and closing the loop:


As a crazy idea, you could monkey patch the underlying event system. This could be wrapped up in elm so that you have a nice interface to hide the evil JS.

    const _addEventListener = EventTarget.prototype.addEventListener;

    EventTarget.prototype.addEventListener = function(type, handler, useCapture) {
        return _addEventListener.apply(this, [
          type === "input" ? debounce(handler, 250) : handler,

This is a simplified version to show the concept

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.