useLocalStorage() React Hook

Never lose a form submission again

James Fulford
May 24 · 5 min read
Image for post
Image for post
Now that’s what I call local storage! Photo by on

Story Time: Moodle Quizzes

Some years ago, I used (comparable to Blackboard or Canvas, if you’ve heard of those). At the time, it was not the prettiest, but it worked well enough, and the school could not complain about the price. I really only had one complaint.

When taking a test/quiz in Moodle, it would pop up a reminder about how much time you had left. Not the smoothest user experience (UX), but understandable at the time. However, if you were typing a response, the text area would suddenly be de-selected. That meant your next keystrokes were interpreted as shortcut keys.

At the time, hitting backspace in Chrome sent you back a page.

This meant every 30 minutes, the reminder window would try to trick you into going back a page. If it succeeded, then when you re-entered the test all your submission answers — hours worth of work — would be gone.

I’ve had it happen 3 times in a row once. Tears. Not hard to imagine that becoming “I hate Math”, “I hate learning online”, “I hate computers”, then “I hate school” with smaller kids.

To say “Awful UX” would be a gross understatement.

The Right UX: GitHub PR Description

Next time you open a pull request, try refreshing the page before hitting the green “Create pull request” button. After refresh and a quick flash, your submission re-appears. I’ve seen it happen for PR review comments as well. You might not even notice it.

Image for post
Image for post
A screenshot doesn’t do this feature justice. Seriously, try it next time you open a PR.

It’s like the ‘Remember Me’ checkbox, but always enabled and not only for usernames.

This feature makes sense only for input fields where users spend considerable time, including long text submissions, lengthy surveys/forms, fields commonly forgotten (like username), or ephemeral preferences. I’d think twice before using this UX for sensitive fields: there’s a reason “Remember Me” remembers your username, not your password.


We can implement this UX by storing the in-progress value on the client-side in localStorage. When the text changes, we can send the text to localStorage. On page load, we can use localStorage to populate the current value.

React Hook

Without further ado, the hook with TypeScript typings ():

Original source:

Controlled forms are built with a useState hook, following the pattern const [currentFieldValue, setCurrentFieldValue] = useState(initialValue); , then displaying currentFieldValue and calling setCurrentFieldValue when the input changes. This is where we would want to insert our localStorage-sync feature, so mimicking the useState interface and semantics would make dropping this into your component easy.

Naming the Key

The key is a string and should contain the identifiers needed to keep it separate from other components, if necessary.

Consider the GitHub pull request use-case above, for example. You would want the key for the PR description to contain the unique identifier for the source repo and branch as well as the target repo and branch, so different pull requests don’t pull up the same description.

If the data is global, then the key can just be a static string.

Data Persistence Can Be Tricky

Data persistence may cause a number of tricky problems.

If you change the structure of the data you persist (because of a UI software update), you may cause the code to break due to unexpected structures. In the backend world of data persistence, we use “migration” scripts to change all data into the right structure.

Fortunately, if you are using this hook for non-critical data, you might be able to get away with ‘forgetting’ the old persistence. Change a part of your localStorage key template so your code misses the old stuff. Something like pull-request-description-cache-${sourceRepo}-${sourceBranch}-${targetRepo}-${targetBranch}-v1 , but then incrementing to v2 when you break your contract. (Put in a quick script somewhere to clean up the v1 keys by iterating over Object.keys(localStorage))

When to clean up localStorage

Clean-up of data is often overlooked, but critical in building a finished product.

Long Form

The Moodle example above is tricky. If two users take the same test in the same browser (one after another), you wouldn’t want the second one to see the first one’s selections.

Consider clearing the relevant localStorage keys once:

  • the form is successfully submitted
  • when a submission is explicitly aborted (cancel button versus accidental navigation)
  • when users logout or login

Sensitive Information

Data in localStorage is retrievable in XSS attacks, so if you have Personally Identifiable Information or other sensitive data in localStorage, consider clearing it out now and then. Store a timestamp for doing any TTL-type calculations. (Use to get a JSON-serializable epoch number)

Don’t rely on data in localStorage for security reasons. Anyone can edit localStorage. Even if you encrypt it — anything in your front-end is visible to your users, including encryption keys. (unless you encrypt it on the backend, then maybe, but realize users can clear their localStorage or switch to a different device).

Ephemeral Preferences

Preferences such as theme or sort order in a list are nice things to store (“ergonomics” is the word that comes to mind), but don’t rely on localStorage if you intend to persist things for a long time. Your app should be OK with users clearing their caches, switching browsers, or logging in on a different device — all situations where the localStorage will not persist.

A note from the Plain English team

Did you know that we have four publications and a YouTube channel? You can find all of this from our homepage at — show some love by giving our publications a follow and !


JavaScript In Plain English

New JavaScript + Web Development articles every day.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store