The prototype works. You added a task, refreshed the page, and it was still there. Then a friend opened the same URL and saw an empty list. Nothing broke. The data was only ever on your laptop, in your browser, and no one else was ever going to see it.

The idea

Every piece of information in your app has a home, and the home decides who can see it and how long it survives. There are three realistic homes for a small project: a file that ships with your code, inside one visitor's browser, and a database on a server. Picking wrong is not fatal, but the cost of moving grows with every row you write.

code
  WHO SEES IT      ->     WHERE IT LIVES        ->    GONE WHEN
  ------------------------------------------------------------------
  everyone,               a file in your repo         you edit it and
  identically             content/*.mdx, .json        redeploy
  ------------------------------------------------------------------
  one person,             localStorage                they clear the
  one browser             ~5 MB, text only            browser or switch
                          browser tab                 to their phone
  ------------------------------------------------------------------
  one person,             SQLite                      the server's disk
  one server              a single .db file           is wiped
  ------------------------------------------------------------------
  everyone, live,         Postgres                    you delete it, and
  many at once            (Supabase, Neon, RDS)       your backups

How it works

A file in your repository is read at build time. It is perfect for things that change when you change them: product pages, blog posts, pricing copy, config. It is public, it is in git, and a visitor can never add to it.

localStorage is a key-value notepad the browser keeps per site, per device, capped around 5 MB, and it only holds text. Good for a theme preference, a dismissed banner, a draft the user has not submitted yet. Bad for anything you need on their other device, anything two people share, and anything you would be upset to lose. The user can open developer tools and edit it, so it is never a place to record what someone paid or what they are allowed to do.

A database is a separate program built to hold changing records for many people at once, answer questions about them quickly, and not lose them when your app restarts. SQLite is a database in a single file on one machine, with no server to run. Postgres runs as its own service and is what Supabase, Neon and most hosts give you. Both speak .

What to do

  1. Before you build a feature, say who owns the data and how long it must live. "Any visitor, until they delete it" means a database. "This one person, until they clear their browser" means localStorage.
  2. Default to Postgres for anything a user creates. Supabase gives you a hosted one in about two minutes, and starting there costs you nothing you would not have paid later.
  3. Keep secrets and personal information out of files in your repository and out of localStorage. Both are readable by people who should not read them.

Where it breaks

The advice inverts for content. Putting your marketing pages in Postgres so they are "editable" adds a database call, a schema and a backup story to something a markdown file did better. SQLite is a serious database, not a toy, and it is a good answer for a single-server app, but it is a bad answer the moment your host runs two copies of your app or throws away the disk between deploys. And a database is not safety by itself. It only protects data you have actually backed up and restricted access to, which is a decision, not a default.