‹ Build Your First Tool Lesson 10 of 16
Contents Lesson 10 of 16

5 min read · foundations

Your own context, next to the numbers

Here your tool stops being a worse version of a public website and becomes something only you have.

A price is available everywhere. Why you are watching this instrument is not available anywhere except in your head, and a tool that holds it beats one that does not, permanently. "Bought at 291, thesis is the services margin" is the note that turns a number into a decision you can review.

Where notes live, and why not in the same file

Your tickers are in src/lib/watchlist.ts and are committed to git. Notes should not be. They change often, they are personal, and some of them will mention money.

Keep them client-side, keyed by ticker:

type Notes = Record<string, { note?: string; tags?: string[] }>;

const KEY = "watchlist-notes-v1";

export function loadNotes(): Notes {
  try {
    return JSON.parse(localStorage.getItem(KEY) ?? "{}");
  } catch {
    return {};        // corrupt storage must not white-screen the tool
  }
}

Three things in that small function.

The v1 in the key. When your shape changes, bump to v2 and you have not silently broken every existing user, including yourself in three weeks.

The try/catch. localStorage can hold anything, including half a write from a crashed tab. JSON.parse throws on that, and an uncaught throw during render is a blank page. This is a two-line defence against a class of bug that is very hard to reproduce on purpose.

This is deliberately temporary. localStorage means this browser, this profile, until something clears it — it does not follow you to another machine, it can be evicted with no event you can catch, and nothing on a server can read it. That is the right trade today, because it buys you a working feature in ten minutes with no backend. Course 4 migrates it to a server store, and the reason it can wait until then is that nothing in courses 1 to 3 needs your notes to exist anywhere but in front of you.

Keyed by ticker, not by index. Index-keyed notes attach to positions, so reordering your list silently moves every note to the wrong instrument. That is data corruption that looks like a UI bug.

The client-side boundary you are now crossing

Until now everything rendered on the server. Notes are typed by a human, so this feature needs a client component, and that is the exact conversion the loop lesson told you to watch for.

Do it deliberately and keep it small: the note editor is a client island; the page stays a server component. The "use client" directive goes on the little component that owns the input, not on the page. If your assistant proposes converting the page, that is the diff to reject, because a client page is a page whose data fetching can drift into the browser.

Tags earn their place only if you use them

Tags are how you ask "show me only the things I am watching for the same reason" once your list outgrows a screen. Two rules keep them from becoming clutter: a small fixed vocabulary you actually reuse, and no tag that duplicates something the data already tells you. sector:tech is derivable from the API. thesis:margins is not, and that is the kind worth typing.

Try it now

Add a note field to one row and reload. Then do the test that matters: open developer tools, corrupt the stored JSON by hand, and reload again. If your tool white-screens, your try/catch is missing or in the wrong place. A tool that dies because of its own saved data is a tool you will stop trusting.