1. Better Web apps: add an Undo button

    May 23rd, 2006

    Because we all make mistakes

    I designed co.mments, so it’s hard to accidentally delete conversations. I did what other Web developers do and added a confirmation popup. It looked good on paper.

    Most times, I really do mean to delete stuff, and confirmation popups just get in the way. When I’m scattered, usually early morning before the coffee kicks in, I end up clicking (and confirming) the wrong stuff. If I’m not paying attention to what I delete, I can’t find it again. Especially when I’m deleting something I added a few days back.

    Software that forces you to think twice before you do anything is software you can’t trust. And software you can’t trust isn’t fun to use.

    So I got rid of the confirmation popup and added an undo button.

    undo.png

    How it works

    It’s surprisingly easy to do. Now I wonder why I didn’t do it before. I’d like to see that in all my favorite apps, so I’m going to share the recipe and code with you. If you’re building the next best Web app, please add this. Or get your favorite geek to add it to their site.

    Each action you do has a URL with all the parameters. I created an undo stack that stores these URLs in the user’s session. Each time you perform an action, the server pushes another undo action URL to the stack. If you just created a new post, the server pushes a ‘delete that post’ URL to the stack.

    An undo button at the top of the page uses the most recent undo action URL. Click on the button and it will perform the ‘delete that post’ action. It also removes that action from the stack, so you can undo the previous action. For co.mments, I set the undo stack to five actions, so you can undo more than one change, but it doesn’t take over the database.

    Since you can perform an action directly or by undoing a previous button, the undo URL includes the parameter undo=true.

    Example using Rails

    Here’s the Rails controller with a create action that creates a new record, a delete action that will undo it, and a before_filter that removes undo actions from the stack:

    # Remove undo action from stack.
    before_filter do |controller|
    controller.undo.pop(controller.params) if controller.params[:undo]
    end
    
    # The create action creates a new record, the undo action deletes it.
    def create()
    # Do something useful here
    record = Record.create(@params)
    # If this is not an undo, add an undo action to delete the record.
    unless @params[:undo]
    undo.push (”Delete newly created record”,
    :action=>”delete”, :id=>record.id)
    end
    # Render page (see below).
    . . .
    end
    
    def delete()
    . . .
    end

    A third method renders the undo form and button, using the last undo action from the stack. The view creates a wrapper element and renders an action:

    <div id="undo"><%= undo.render %></div>

    For AJAX requests, I use RJS to instruct the browser to update the undo button without reloading:

    render :update do |page|
    page["undo"].replace_html undo.render
    end

    Get the code

    I wrapped the code as a Rails plugin. To install the plugin into your Rails app:

    ./script/plugin source http://labnotes.org/svn/public/ruby/rails_plugins/
    ./script/plugin install undo_helper

    You can find the source code here. You can also try it out on the co.mments server.

    Update: I forgot to mention that GMail has had undo for a while, although only for the last action. Hans posted a screen shot of undo on Technorati.

    1. Anonymous

      Annulla l’ultima azione… nel web!…

      Una classe scritta in Ruby on Rails per annullare l’ultima azione, perché ‘we all make mistakes’…….

    2. hans.gerwitz

      Good UI sighting…

      Technorati has a wonderful solution for shielding users from accidental destructive action: offer an undo link along with the response message:

      This is much less disruptive than an “are you sure?” dialog, which are often ignored by confirm…

    3. Luke

      Hmm, GET requests are not supposed to have any side effects. Your app will break horribly with things like web accelerators. But I’m sure your method should be relatively easy to fix — just serialise the POST parameters instead of the URL.

    4. Assaf

      @Luke

      You’re right about GET, it should not be used for requests that have side effect. This code uses POST, while passing the data in the query string.

    5. undo_helper: Add ‘Undo’ to your Rails application

      [...] The creator of co.mments, Assaf Arkin, a Rails developer, has created a plugin that makes it easy to add a Undo feature to your Rails app. Instead of warning users about things simply let them do it, but give them the choice to undo afterwards. Assaf’s plugin doesn’t cover all the bases, but it’s easy to build upon for creating your own system. For deletion, for example, you’d want to come up with a way to keep the data floating around for a while. [...]

    6. johan

      Is it possible to undo a delete or an update action ?

    7. Assaf

      Johan,

      I do that in co.mments. If you delete or update, it stores a POST request to create again, or update to the old value.

    8. Terry Darnell

      I need to remove pop up blockers

    9. Andy Stewart

      This is a big improvement in terms of user experience. I wonder, though, how you cope with deleting a record which in turn cascade-deletes many dependent records too?

      I suppose you would have to be able to push a group of creation actions onto the stack as a single unit. Does that sound right?

    10. Assaf

      Andy, that’s one possibility.

      Another possibility, mark items as deleted an unmark when undone (what I’m using in one of my projects). That would also work better for large objects.

      If the objects are not particularly larger but are complicated, you can also serialize them as blobs into an undo table, which can aggregate undos for several types of objects.

    11. Labnotes » Rounded Corners - 128 (lolcathost)

      [...] Undo, redone. Aza Raskin reminds us to Never Use a Warning When you Mean Undo. The article got a lot of attention the past few days, which is great. It’s often a simple change that makes apps much easier to use. As a reminder, and for those who like the simplest thing that could possibly work, here’s my implementation of an Undo button for Rails. [...]

    12. Contrast | The Blog | 37signals’ biggest flaw

      [...] Better Web apps: add an Undo button. [...]

    Leave a Reply | Trackback | Track with co.mments

    Where's my comment? I get too much comment spam, so I have to moderate comments. Damn those spammers. If you don't see your comment immediately, be patient. I'll approve it the minute I see it. Want to know when your comment shows up, or check if anyone responded? Track it.

    Or using OpenID