A lot of habit apps fail the same way: the first screen asks for too much. A blank page, a mood score, five questions. On a bad day you close it and never come back.
I wanted to see how small a gratitude log could get and still be useful, so I wrote one in plain JavaScript. It keeps three short entries per day in localStorage, shows the last week, and that is it. Here is the whole thing and what I learned building it.
The code
<form id="f">
<input name="a" placeholder="One small good thing" required>
<input name="b" placeholder="Another">
<input name="c" placeholder="And one more">
<button>Save today</button>
</form>
<ul id="log"></ul>
<script>
const KEY = "gratitude-log";
const today = () => new Date().toLocaleDateString("en-CA"); // YYYY-MM-DD in local time
const load = () => JSON.parse(localStorage.getItem(KEY) || "{}");
function render() {
const data = load();
const days = Object.keys(data).sort().reverse().slice(0, 7);
document.getElementById("log").innerHTML = days
.map(d => `<li><b>${d}</b>: ${data[d].map(s => s.replace(/</g, "<")).join(" · ")}</li>`)
.join("");
}
document.getElementById("f").addEventListener("submit", e => {
e.preventDefault();
const entries = [...new FormData(e.target).values()].map(s => s.trim()).filter(Boolean);
const data = load();
data[today()] = entries; // one record per day; saving again overwrites
localStorage.setItem(KEY, JSON.stringify(data));
e.target.reset();
render();
});
render();
</script>
About 30 lines. No build step, no account, nothing leaves the browser.
Three decisions that mattered more than the code
1. Only the first field is required. On a hard day, one line is a win. Requiring all three turned out to be the fastest way to make me skip a day entirely.
2. Use the local date, not UTC. new Date().toISOString() gives you a UTC date, so an entry written at 8am in Tokyo lands on "yesterday". toLocaleDateString("en-CA") returns YYYY-MM-DD in the user's own time zone, which is what "today" means to a person.
3. Overwrite instead of append. Saving twice on the same day replaces the record. That keeps the data model one key per day and avoids a log full of half-finished duplicates.
Where this breaks
-
localStorageis per browser and per device. Clear site data and the log is gone. For anything you care about, export it (aJSON.stringify(load())download button is five more lines). - There is no reminder. In my experience the reminder, not the form, is what keeps the habit alive, and a static page cannot send one reliably.
- I deliberately left out streaks. They motivate some people and make others quit after the first missed day. If you add one, consider showing "days this month" instead of an unbroken count.
If you would rather not build it
Disclosure: I make Thankful, a free iPhone gratitude journal built around the same idea: three short entries, about two minutes, plus a daily affirmation. It does have a reminder and a streak count, so it trades a little of the minimalism above for the nudge. Thankful on the App Store.
Otherwise, the snippet above is free to copy. Three lines a day is enough.
Top comments (0)